[
  {
    "path": ".gitmodules",
    "content": "[submodule \"repackPOC/androguard\"]\n\tpath = repackPOC/androguard\n\turl = https://github.com/androguard/androguard.git\n"
  },
  {
    "path": "COPYRIGHT",
    "content": "Copyright 2014 Luca Falsina\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License."
  },
  {
    "path": "README.md",
    "content": "# ![Logo](https://github.com/lukeFalsina/Grab-n-Run/raw/master/gnr/app/src/main/res/drawable-mdpi/logo_with_name.png)\r\n\r\n## Research paper\r\n\r\nWe present the findings of this work in a research paper:\r\n\r\n**Grab'n Run: Secure and Practical Dynamic Code Loading for Android Applications**  \r\nLuca Falsina, Yanick Fratantonio, Stefano Zanero, Christopher Kruegel, Giovanni Vigna, Federico Maggi.  \r\n*In Proceedings of the Annual Computer Security Applications Conference (ACSAC). Los Angeles, CA December, 2015*\r\n[[PDF](http://cs.ucsb.edu/~yanick/publications/2015_acsac_grabandrun.pdf)]\r\n[[Bibtex](https://github.com/lukeFalsina/Grab-n-Run/raw/master/grabandrun.bib)]\r\n\r\nIf you use *Grab'n Run* in a scientific publication, we would appreciate citations to the previous paper.  \r\nPlease use this **Bibtex** entry:\r\n``` tex\r\n@InProceedings{falsina15:grabandrun,\r\n  author = {Luca Falsina and Yanick Fratantonio and Stefano Zanero and Christopher Kruegel and Giovanni Vigna and Federico Maggi},\r\n  title = {{Grab'n Run: Secure and Practical Dynamic Code Loading for Android Applications}},\r\n  booktitle = {Proceedings of the Annual Computer Security Applications Conference (ACSAC)},\r\n  month = {December},\r\n  year = {2015},\r\n  address = {Los Angeles, CA}\r\n}\r\n``` \r\n\r\n## News\r\n\r\n- *10/10/2015* - The **repackaging tool** is now **[online](http://grab-n-run.readthedocs.org/en/latest/repackaging.html)**. Use it to patch automatically your applications to use Grab'n Run APIs. \r\n- *01/17/2015* - **Grab'n Run** is now **available** on [JCenter](https://bintray.com/bintray/jcenter?filterByPkgName=grab-n-run)\r\n- *01/16/2015* - **Grab'n Run** project migrates to [Android Studio](http://developer.android.com/tools/studio/index.html), the official *IDE* for **Android application development**. However, you can still use the library also with your *ADT* projects! (*see below the \"Quick Setup\" section for further details*)\r\n- *11/26/2014* - **Grab'n Run is on line!**\r\n\r\n## Introduction\r\n\r\n*Grab’n Run* (aka **GNR**) is a **simple** and **effective** Java Library that you can easily add to your Android projects to perform *secure dynamic class loading* operations over standard [DexClassLoader](http://developer.android.com/reference/dalvik/system/DexClassLoader.html).\r\n\r\nPrevious research has shown that many applications often need to perform dynamic class loading to implement, for example, non-invasive self-update features. However, research has also shown that it is really challenging to *safely* implement these features. This is of particular importance as, in this context, **one single mistake** could open the application (and, therefore, the entire device) to **serious security vulnerabilities**, such as *remote code execution*.  \r\n\r\nThe main goal of *Grab's Run* is to offer an alternative to the native Android APIs, and its design enforces that even the most inexperienced developer cannot perform well-known, serious mistakes.\r\n\r\nFor a **brief presentation** of the library and some of its features you can give a look at these [slides](https://goo.gl/QrwWey), while if you prefer a more **structured and complete description** with *set up information, tutorials, examples, tips&tricks, and a full presentation of the API* you should definitely check the [documentation](http://grab-n-run.readthedocs.org/en/latest/).\r\n\r\nIf you desire to suggest new *features, improvements, criticisms* or whatever, I would be more than glad to hear **any kind of constructive feedback** :D  \r\nYou can contact me either by dropping an email at [lfalsina@gmail.com](mailto:lfalsina@gmail.com) or by pinging me on Twitter [@lfalsina](https://twitter.com/lfalsina).\r\n\r\n## Main features\r\nSecurely load code dynamically into your Android application from **APK** containers or **JAR** libraries translated to be *executable by both the Dalvik Virtual Machine (DVM) and the Android Runtime (ART)* (don't worry a [section](http://grab-n-run.readthedocs.org/en/latest/complementary.html#on-library-developer-side-how-to-prepare-a-valid-library-container-compatible-with-gnr) of the docs explains step-by-step how to do it).\r\n\r\n- *JAR* and *APK* containers can be either already stored on the device or **automatically fetched from remote locations** by GNR.\r\n- Retrieved containers signatures are compared against a **valid developer certificate**. Only containers that are **correctly signed** are allowed to have their classes loaded dynamically. This ensures **integrity** and **developer authentication** on all the retrieved containers.\r\n- Developer certificates are retrieved from remote locations securely and cached on the mobile phone for future verifications.\r\n- *Cached classes, containers and certificates* used for the signature verification are stored into *application-private* folders. This **prevents** your application **from code injection attacks** at runtime.\r\n- GNR implements an **effective caching system** that speeds up its execution and at the same time enables it to *work in most cases also when no connectivity is available*.\r\n- Transition to GNR is **smooth** for the application developer since its **API** where thought to be *as close as possible to the standard API* provided by the Android framework.\r\n- When *many containers* are provided as sources for class loading, *Grab'n Run* performs a **concurrent multi-thread signature verification** in order to *limit the performance overhead*.\r\n- GNR helps the application developer to **implement silent updating** on *remote third-party libraries in a secure and concise way*. \r\n\r\n## Quick Setup\r\n\r\nThis section explains how to setup *Grab'n Run* as a library for your Android applications.\r\n\r\n#### 1. Include library\r\n\r\n###### a. Android Studio (AS)\r\n\r\n* Modify the *build.gradle* file in the *app* module of your Android project by adding the following *compile* line in the *dependencies* body:\r\n``` gradle\r\ndependencies {\r\n    // Grab'n Run will be imported from JCenter.\r\n    // Verify that the string \"jcenter()\" is included in your repositories block!\r\n    compile 'it.necst.grabnrun:grabnrun:1.0.4'\r\n}\r\n``` \r\n* Resync your project to apply changes.\r\n\r\n###### b. Android Development Tool (ADT)\r\n\r\n* [Download JAR](https://github.com/lukeFalsina/Grab-n-Run/raw/master/downloads/1.0.4/grabnrun-1.0.4.jar)\r\n* Put the JAR in the **libs** subfolder of your Android project\r\n\r\n#### 2. Android Manifest\r\n\r\nModify the *Android Manifest* of your application by adding a couple of **required permissions**:\r\n``` xml\r\n<manifest>\r\n\t<!-- \tInclude following permission to be able to download remote resources \r\n\t\t\tlike containers and certificates -->\r\n\t<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\r\n\t<!-- \tInclude following permission to be able to download remote resources \r\n\t\t\tlike containers and certificates -->\r\n\t<uses-permission android:name=\"android.permission.INTERNET\" />\r\n\t<!-- \tInclude following permission to be able to import local containers \r\n\t\t\ton SD card -->\r\n\t<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\r\n\t...\r\n</manifest>\r\n```\r\n\r\n## Quick example of use\r\n\r\nThis quick use case gives you a taste on how to use GNR once you have added it to your project.\r\n\r\n#### 1. Create a key pair to sign your code and export your developer certificate\r\n\r\n* Open a terminal and type the following command to **generate a keystore** and a **keypair**:\r\n``` bash\r\n$ \tkeytool -genkey -v -keystore my-tests-key.keystore -alias test_dev_key \r\n\t-keyalg RSA -keysize 2048 -validity 10000\r\n```\r\n* Next **export** the public key **into a certificate** that will be *used to verify your library code* before dynamically loading it:\r\n``` bash\r\n$\tkeytool -exportcert -keystore my-tests-key.keystore -alias test_dev_key \r\n\t-file certificate.pem\r\n```\r\n* You should now see in the folder a **certificate file** called *certificate.pem*\r\n\r\n#### 2. Publish your developer certificate on line at a remote location which uses HTTPS protocol\r\n\r\nYou can publish the certificate wherever you like as long as **HTTPS** protocol is used and **everyone can access this location** from the web.\r\nAs a **test** example you could store the *certificate.pem* in your \"Public\" *Dropbox* folder and then retrieve the **associated public link**, which could be for example something like \"https://dl.dropboxusercontent.com/u/00000000/certificate.pem\". Note this URL down, you will need it soon.\r\n\r\n#### 3. Export an unsigned container and sign it with your developer key\r\n\r\nLet's say that in your IDE (i.e., the *Android Development Tools (ADT)*) you have an Android project called **\"LoaderApp\"** from which you want to load some of its classes dynamically in another project.\r\n\r\n* In the *ADT Package Explorer* **right** click on **\"LoaderApp\"** -> Android Tools -> Export Unsigned Application Package...\r\n![Screenshot](https://github.com/lukeFalsina/Grab-n-Run/raw/master/docs/images/ExportUnsignedContainer.png)\r\n* Next select the **same folder** where you have previously saved the keystore and the keypair as the *destination folder* and press OK.\r\n* Open a terminal which points to the destination folder and **sign the apk container** with the previously created key:\r\n``` bash\r\n$\tjarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 \r\n\t-keystore my-tests-key.keystore LoaderApp.apk test_dev_key\r\n```\r\n* Finally **align** the apk container to optimize access time to its resources:\r\n``` bash\r\n$\t<path_to_your_sdk>/sdk/build-tools/<sdk_version_number>/zipalign -v 4 \r\n\tLoaderApp.apk LoaderAppAligned.apk\r\n```\r\n\r\n**P.S.** *Step 3* can also be directly performed by means of your favorite *IDE*. In **ADT** you would have to select the option *\"Android Tools -> Export Signed Application Package...\"* and, when it is required, navigate to the location of your keystore and inserting its password, the key id and the key password. On the other hand in **Android Studio** the signature process can be automatized by setting up a proper **signing configuration** as described [here](http://developer.android.com/tools/publishing/app-signing.html#release-mode).\r\n\r\n#### 4. Publish the signed and aligned version of the source container\r\n\r\nOnce you have obtained *LoaderAppAligned.apk*, you need to make also this resource **available on line**. Notice that, in this case, both remote locations that use **HTTP** or **HTTPS** protocols are fine as long as they are accessible from the web. Again, as an example, you can store the container in your \"Public\" *Dropbox* folder and get back a **public URL** like \"https://dl.dropboxusercontent.com/u/00000000/LoaderAppAligned.apk\".\r\n\r\n#### 5. Set up dynamic code loading with GNR in the application\r\n\r\nIn the end, it is time to set up a *SecureDexClassLoader* instance to **fetch your remote container and developer certificate**, **store it in a safe place** and **perform a signature verification** before dynamically loading your code.\r\n\r\n**Copy and paste** the code below in one of the Activity in your target Android project, where you have *already imported GNR*, to **dynamically and securely load** an instance of the class *\"com.example.MyClass\"*:\r\n``` java\r\n\r\nMyClass myClassInstance = null;\r\njarContainerPath = \"https://dl.dropboxusercontent.com/u/00000000/LoaderAppAligned.apk\";\r\n\r\ntry {\r\n\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\r\n\tpackageNamesToCertMap.put(\"com.example\", new URL(\"https://dl.dropboxusercontent.com/u/00000000/certificate.pem\"));\r\n\r\n\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\r\n\tSecureDexClassLoader mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\tjarContainerPath, \r\n\t\t\t\t\t\t\t\t\t\t\t\tnull, \r\n\t\t\t\t\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\r\n\t\t\t\t\t\t\t\t\t\t\t\tpackageNamesToCertMap);\r\n\t\t\r\n\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(\"com.example.MyClass\");\r\n\r\n\t// Check whether the signature verification process succeeded\r\n\tif (loadedClass != null) {\r\n\r\n\t\t// No security constraints were violated and so\r\n\t\t// class loading was successful.\r\n\t\tmyClassInstance = (MyClass) loadedClass.newInstance();\r\n\t\t\t\t\r\n\t\t// Do something with the loaded object myClassInstance\r\n\t\t// i.e. myClassInstance.doSomething();\r\n\t}\r\n\r\n} catch (ClassNotFoundException e) {\r\n\t// This exception will be raised when the container of the target class\r\n\t// is genuine but this class file is missing..\r\n\te.printStackTrace();\r\n} catch (InstantiationException e) {\r\n\te.printStackTrace();\r\n} catch (IllegalAccessException e) {\r\n\te.printStackTrace();\r\n} catch (MalformedURLException e) {\r\n\t// The previous URL used for the packageNamesToCertMap entry was a malformed one.\r\n\tLog.e(\"Error\", \"A malformed URL was provided for a remote certificate location\");\r\n}\r\n```\r\n\r\n*Et voilà..* now you have an instance of *\"MyClass\"* loaded in a **secure way** at **run time**!\r\n\r\n## Next steps :)\r\n\r\n* If you want to learn how to use *Grab'n Run* I suggest to start from the [tutorial](http://grab-n-run.readthedocs.org/en/latest/tutorial.html) and then moving on by analyzing the [example application](http://grab-n-run.readthedocs.org/en/latest/example.html).\r\n* If you are interested in understanding what are the **security threats** of *improper dynamic code loading* fixed by GNR check out the [security resume](http://grab-n-run.readthedocs.org/en/latest/security.html).\r\n* If you would like to implement cool features of GNR like **silent updates**, **handling more containers**, **concurrent code loading** or **dynamically loading JAR libraries in your applications** you should give a look at the [complementary topics](http://grab-n-run.readthedocs.org/en/latest/complementary.html).\r\n* You may also need to **consult** the *JavaDoc-like* [API documentation](https://rawgit.com/lukeFalsina/Grab-n-Run/master/docs/javaDoc/index.html).\r\n* Finally, you may want to convert automatically your applications to use Grab'n Run APIs for secure dynamic code loading. Give a try at the [repackaging tool](http://grab-n-run.readthedocs.org/en/latest/repackaging.html).\r\n\r\n## License\r\n\r\n*Grab'n Run* is released under the *Apache* license. Check the *COPYRIGHT* file for further details.\r\n\r\n[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Grab--n--Run-brightgreen.svg?style=flat)](https://android-arsenal.com/details/1/1185)\r\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "/_build/\n/javaAPI/\nmake.bat\n"
  },
  {
    "path": "docs/.placeholder",
    "content": ""
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = _build\n\n# User-friendly check for sphinx-build\nifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)\n$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)\nendif\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\trm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/docgrabnrun.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/docgrabnrun.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/docgrabnrun\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/docgrabnrun\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n"
  },
  {
    "path": "docs/complementary.rst",
    "content": "Complementary topics\n====================\n\nIn the end of this documentation a couple of not so trivial use cases of *Grab'n Run* are presented. This section will not introduce new core concepts but it may help the developer to handle some **tricky situations**. For such a reason feel free to **skip this part** and eventually **come back later** to revise it whenever you will encounter one of the following situations while using the library.\n\nHandle containers whose classes come from different package names which have a common relevant prefix\n-----------------------------------------------------------------------------------------------------\n\nBefore starting diving in this section it is important to recall the **relationship between package name and containers**. \n\n**Package name in apk containers**\n\t*Apk* containers must contain just **one package name**, which must be chosen by the developer when a new application is created. The package name is then stored in the *Android Manifest* of the application. In order to have an application being admitted on the *Google Play* store, it is also fundamental that the chosen package name is **unique** and should **not change** for the whole life cycle of the application.\n\n**Package name in jar containers**\n\t*Jar* containers on the other hand do **not** have such a **strict policy** as in *apk* containers. Hypothetically each class file contained in a *jar* archive may have a different package name and this mean that **many package names** can be present in the **same** *jar* container.\n\n**Common relevant prefix**\n\tIn *Grab'n Run* two package names share a relevant common prefix if their prefix match for at least two words separated by one dot.\n\t\n\t**Example:**\n\tConsider the following package names: \n\n\tA. ``com.example.polimi``\n\tB. ``it.example.polimi``\n\tC. ``com.test``\n\tD. ``com.example.application.system``\n\tE. ``com.example.polimi.system``\n\n\t* A. and B. do **not** share any **common relevant prefix** since they differ in the initial word of the package name (``com`` vs ``it``).\n\t* A. and C. do **not** share any **common relevant prefix** since they just have one word of the package name in common (``com``).\n\t* A. and D. share a **common relevant prefix** (``com.example``).\n\t* A. and E. share a **common relevant prefix** (``com.example.polimi``).\n\nGiven these insights a first interesting situation to consider is when a developer wants to *load dynamically classes* from an external *jar* library which contains **more than one package name** that, anyway, share a **common relevant prefix**. Let us assume for example that the target library has the following structure:\n\n.. image:: images/JarContStructure.png\n\nIn such a scenario we have four classes (``ClassA``, ``ClassB``, ``ClassC``, ``ClassD``) which belongs to **three different packages**, whose names are respectively ``com.example``, ``com.example.system`` and ``com.example.system.preference``. Let use also assume that this container has being signed with a *valid self-signed certificate*, remotely located at ``https://something.com/example_cert.pem``.\n\nQuestions now for the developer are:\n\n1. *How should I fill in the associative map which links package names to remote certificate location in order to being able to load all the classes in this container?*\n2. *Am I obliged to insert all three package names pointing to the very same certificate?*\n\nLuckily the answer for the second question is **no**, which means that there is indeed an **easier way** to perform the job. *Grab'n Run* in fact was thought to make the whole dynamic class loading **secure but** at the same time **simple** for applications developers.\n\nYou can in fact handle this situation correctly by simply inserting into the associative map a **single entry** where the *key corresponds to the shortest among the package names* belonging to one of the classes that need to be loaded and the *value is the location of the remote certificate* used to sign the container. So in the **previous case** since the classes with the shortest package name are ``com.example.ClassA`` and  ``com.example.ClassB`` the following code is appropriate to populate the map::\n\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\n\t\ttry {\n\t\t\tpackageNamesToCertMap.put(\t\"com.example\", \n\t\t\t\t\t\t\tnew URL(\"https://something.com/example_cert.pem\"));\n\n\t\t} catch (MalformedURLException e) {\n\t\t\t\n\t\t\t// The previous entry for the map may not necessarily be the right one \n\t\t\t// but still it is not malformed so no exception should be raised.\n\t\t\tLog.e(TAG_MAIN, \"A malformed URL was provided for a remote certificate location\");\n\t\t\t\n\t\t}\n\nFor the rest the developer may proceed as shown in :ref:`Using SecureDexClassLoader to load dynamic code securely`. The result will be that the container is going to be verified against the appropriate certificate and, if it is **genuine**, it will be *also possible to load the other two classes* in the archive with a **different package name** (``com.example.system.ClassC`` and ``com.example.system.preference.ClassD``).\n\nHandle containers whose classes come from different package names with no relevant common prefix\n------------------------------------------------------------------------------------------------\n\nEven if it is not such a common situation it is possible for a *jar* archive to *contain classes which belongs to different package names* and does not share any common relevant prefix.\nThis situation, on the other hand, is **not practical** for *apk* containers since, in order to be **published** on Google Market, \nan application needs to have a **single** package name which more over must **not change** during its whole life cycle.\n\nAnyway let us try to sketch the case of the previous cited jar archive and how to handle it with ``SecureDexClassLoader``. As an example we can consider the \nscenario in which the goal is loading two classes, whose full class names are respectively ``com.example.MyFirstClass`` and ``com.test.MySecondClass`` and so \nwhich **differs** in the **package name** but are **both stored** in the **same container** ``exampleJar.jar``.\nIt is also supposed that this container has being signed with a *valid self-signed certificate*, remotely located at ``https://something.com/example_cert.pem``.\n\nIn order to handle this situation correctly the developer is required to fill the **associative map** which links package names and certificates\nwith **two entries**, one per each package name, which will *point to the same remote certificate*. This is exemplified in the following snippet of code::\n\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\n\t\ttry {\n\t\t\tpackageNamesToCertMap.put(\t\"com.example\",\n\t\t\t\t\t\t\tnew URL(\"https://something.com/example_cert.pem\"));\n\t\t\tpackageNamesToCertMap.put(\t\"com.test\",\n\t\t\t\t\t\t\tnew URL(\"https://something.com/example_cert.pem\"));\n\n\t\t} catch (MalformedURLException e) {\n\t\t\t\n\t\t\t// The previous entries for the map may not be necessarily the right ones \n\t\t\t// but still they are not malformed so no exception should be raised.\n\t\t\tLog.e(TAG_MAIN, \"A malformed URL was provided for a remote certificate location\");\n\t\t\t\n\t\t}\n\nFor the rest the developer may proceed as shown in :ref:`Using SecureDexClassLoader to load dynamic code securely` and this procedure grants to succeed in the loading\nprocess for any of the two classes independently on the order in which they are attempted to be loaded.\n\n.. note::\n\tBy design ``SecureDexClassLoader`` assumes that **each package name** is intrinsically related to a **single container**, while it is not necessary true the opposite.\n\tThis means that attempting to *load a class*, whose **package name** is associated with **more than one container** provided in *dexPath* (i.e. each one of the two \n\tcontainers contains at least one class with the same package name), will generate an **unpredictable behavior** since ``SecureDexClassLoader`` will associate \n\tthat package name with just one of the two containers.\n\n\tSo it is a **developer responsibility** to check the containers in order to avoid the occurrence of this rare but undesirable situation.\n\n.. _Reverse package name to obtain remote certificate URL:\n\nReverse package name to obtain remote certificate URL\n-----------------------------------------------------\n\n*Grab'n Run* provides as an extra feature the possibility to **reconstruct the remote URL location of the certificate by reversing the package name** provided into the associative map. To enable this feature simply add an entry to the associative map where the **key** is the **desired package name to reverse** and the **value** is ``null``.\nHere is a simple snippet of code to exemplify::\n\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\n\t\t// Notice that a null entry won't raise a MalformedURLException..\n\t\tpackageNamesToCertMap.put(\"it.polimi.necst.mylibrary\", null);\n\nWhat is going on behind the curtains is that whenever GNR find an entry with *a valid package name associated to a null value*, it will **reverse the package name** with the following convention:\n\n\tThe **first word** of the package name will be considered as the **top level domain (TLD)**, while the **second** one is going to be the **main domain**. Any **following word** of the package name will be used in the **same order** as they are listed to define the **file path** on the remote server and of course since a secure connection is needed for the certificate, **HTTPS protocol** will be enforced.\n\nLet us translate this theory with some concrete examples:\n\n* Package name ``it`` won't be reverted since it contains just a world (at least two are required for real world package name).\n* Package name ``it.polimi`` will be reverted to the URL ``https://polimi.it/certificate.pem``.\n* Package name ``it.polimi.necst.mylibrary`` will be reverted to the URL ``https://polimi.it/necst/mylibrary/certificate.pem``.\n\nAs you can see from the previous examples this naming convention assumes that the **final certificate** will be found in the *remote folder obtained by reverting the package name* and that the **certificate file** will have been **always renamed** ``certificate.pem``.\n\nPerform dynamic code loading concurrently\n-----------------------------------------\n\n.. warning::\n\tBefore approaching this paragraph, a good idea is having **first read** the :doc:`security` section of this documentation and in particular the last part on performance-related topics.\n\nBy default when a new ``SecureDexClassLoader`` object is instantiated, it will immediately **validate** all of its **containers concurrently** (**Eager signature verification strategy**). By the way sometimes when a large number of containers are assigned to a single ``SecureDexClassLoader`` object, it may just be *more convenient to evaluate each container separately just before loading classes from it*. So in such a scenario a **lazy signature verification strategy** would be advisable.\n\nAn even better *performance concern strategy* is loading target classes in a **concurrent** way on different threads. This is perfectly fine with *Grab'n Run* since the library is **thread-safe**.\n\nAs an example let us consider the case in which we want to *concurrently load some classes with a lazy strategy* from a ``SecureDexClassLoader`` instance with many containers associated to it. A possible code implementation which also makes use of `Executors <https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html>`_ , `FixedThreadPool <https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool(int)>`_ and `Future <https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html>`_ classes is the following::  \n\n\t// Make the assumption that packageNamesToCertMap has been already initialized;\n\t// moreover longListOfDexPath is the String with all the containers path listed and\n\t// separated by :\n\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\t// Initialize a SecureDexClassLoader instance in LAZY mode.\n\tSecureDexClassLoader mSecureDexClassLoader = \n\t\tmSecureLoaderFactory.createDexClassLoader(\tlongListOfDexPath, \n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\n\t\t\t\t\t\t\t\tpackageNamesToCertMap, \n\t\t\t\t\t\t\t\ttrue);\n\n\t// Suppose these classes belongs only to three different containers;\n\t// while longListOfDexPath points to ten containers..\n\tString[] classesToLoad = new String[] {\t\"com.example.classA\",\n\t\t\t\t\t\t\"it.polimi.classB\",\n\t\t\t\t\t\t\"de.application.classC\",\n\t\t\t\t\t\t\"com.example.classD\",\n\t\t\t\t\t\t\"it.polimi.classE\"};\n\n\t// Suppose to store the loaded classes here..\n\tSet<Class<?>> loadedClassesSet = Collections.synchronizedSet(new HashSet<Class<?>>());\n\n\t// Initialize the thread pool executor with number of thread \n\t// equals to the number of classes to load..\n\tExecutorService threadLoadClassPool = Executors.newFixedThreadPool(classesToLoad.size());\t\t\t\n\tList<Future<?>> futureTaskList = new ArrayList<Future<?>>();\n\t\t\t\n\tIterator<String> classesToLoadIterator = classesToLoad.iterator();\n\t\t\t\n\twhile (classesToLoadIterator.hasNext()) {\n\t\t\t\t\n\t\tString classNameToLoad = classesToLoadIterator.next();\n\t\t\t\n\t\t// Submit a new class load thread on a container and store \n\t\t// a reference in the future objects list.\n\t\tFuture<?> futureTask = \n\t\t\tthreadLoadClassPool.submit(new classLoadingTask(mSecureDexClassLoader, \n\t\t\t\t\t\t\t\t\tclassNameToLoad,\n\t\t\t\t\t\t\t\t\tloadedClassesSet));\n\t\tfutureTaskList.add(futureTask);\n\t}\n\t\t\t\n\t// Stop accepting new tasks for the current threadLoadClassPool\n\tthreadLoadClassPool.shutdown();\n\t\t\t\n\tfor (Future<?> futureTask : futureTaskList) {\n\t\t\t\t\n\t\ttry {\n\t\t\t\t\t\n\t\t\t// Wait till the current task for class loading is finished..\n\t\t\tfutureTask.get();\n\t\t\t\t\t\n\t\t} catch (InterruptedException | ExecutionException e) {\n\t\t\t\t\t\n\t\t\t// Issue while executing the verification on a thread\n\t\t\te.printStackTrace()\n\t\t}\n\t}\n\t\t\t\n\ttry {\n\t\t\t\t\n\t\t// Join all the threads here.. Use a timeout eventually..\n\t\tthreadLoadClassPool.awaitTermination(\tKEEP_ALIVE_NUMBER_OF_TIME_UNITS,\n\t\t\t\t\t\t\tKEEP_ALIVE_TIME_UNIT);\n\t} catch (InterruptedException e) {\n\t\t\t\t\n\t\t// One or more of the threads objects were still busy..\n\t\t// And this should not happen..\n\t\te.printStackTrace()\n\t}\n\nAnd finally here it is the ``classLoadingTask``, an implementation of the `Runnable <https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html>`_ interface, which is responsible for **dynamically loading** a single class with the previously created ``SecureDexClassLoader`` instance.  Here is the class implementation::\n\n\tclass classLoadingTask implements Runnable {\n\n\t\t// The shared instance of SecureDexClassLoader for concurrent load ops.\n\t\tprivate SecureDexClassLoader mSecureDexClassLoader;\n\t\t// The name of the class to load.\n\t\tprivate String classNameToLoad;\n\t\t// Concurrent set of class objects that were successfully loaded.\n\t\tprivate Set<String> successLoadedClassesSet;\n\t\t\n\t\tpublic classLoadingTask(\tSecureDexClassLoader mSecureDexClassLoader, \n\t\t\t\t\t\tString classNameToLoad, \n\t\t\t\t\t\tSet<String> successLoadedClassesSet) {\n\t\t\t\n\t\t\t// Simply copy all the incoming parameters..\n\t\t\tthis.mSecureDexClassLoader = mSecureDexClassLoader;\n\t\t\tthis.classNameToLoad = classNameToLoad;\n\t\t\tthis.successLoadedClassesSet = successLoadedClassesSet;\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic void run() {\n\t\t\t\n\t\t\t// Set current thread priority to DEFAULT.\n\t        \tandroid.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT);\n\t\t\t\n\t\t\ttry {\n\n\t\t\t\t// Load operation is invoked..\n\t\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(classNameToLoad);\n\n\t\t\t\t// Check whether the loading operation succeeds\n\t\t\t\tif (loadedClass != null) {\n\n\t\t\t\t\t// Class loading was successful and performed in a safe way.\n\t\t\t\t\t// Add this class to the concurrent set\n\t\t\t\t\tsuccessLoadedClassesSet.add(loadedClass);\n\t\t\t\t}\n\n\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\t// This exception will be raised when the container of the \n\t\t\t\t// target class is genuine but this class file is missing..\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (InstantiationException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (IllegalAccessException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\nThe interesting **advantage** of this *concurrent evaluation* is that **only the first loaded class** belonging to each separate container will perform the **signature verification** process when the ``loadClass()`` method is invoked, while all the other loaded classes from the same container will benefit from the cached result of this verification and so their evaluation  will be way faster (comparable to the ``loadClass()`` time execution of ``DexClassLoader``).\n\n.. note::\n\tUsing this **concurrent lazy approach** is a good way to *lower the performance overhead* that may be introduced by *Grab'n Run* and *keep your application always responsive*. Another slight shrewdness that you may consider when you are in need to *load many classes from containers that have to be downloaded* is considering to show a `ProgressDialog <http://developer.android.com/reference/android/app/ProgressDialog.html>`_ or a similar object to *make the user aware that your application is performing some tasks that require him/her to wait* and at the same time prevent the user from clicking everywhere or terminating your application since it sometimes may seem not fully responsive.\n\n.. * By now use SecureDexClassLoader in Lazy mode. Instantiate such an object on the main thread.\n.. * Initialize a thread executor and then makes each thread load a class from the same SecureDexClassLoader object. Evaluation of containers will be performed only by the first thread to load a class into a container while the others will use the cached verification mechanism to directly load or reject loading for their target class.\n.. * Remember to put a join instruction at the end of the code block on the main thread to be sure that after that line all the classes that you need have attempted to being loaded.\n\nOn library developer side: how to prepare a valid library container compatible with GNR\n---------------------------------------------------------------------------------------\n\nFor once in this tutorial the **focus is now moved** from the *application developer*, who wants to load classes from an external library, **to the library developer**, who wrote a library and wants to make it available to the application developers.\n\nWhat we are going to discuss about in this section is **how a library developer should prepare his/her library** in order to have it **compatible with GNR** system and more in general with **dynamic code loading**. A hint in this sense is provided by DexClassLoader `documentation <http://developer.android.com/reference/dalvik/system/DexClassLoader.html>`_, which states clearly that this class, and so also ``SecureDexClassLoader`` does, *\"loads classes from .jar and .apk files containing a classes.dex entry.\"*.\n\n.. note::\n\tThe procedure outlined below must be performed entirely in case that you want to **export a library** into a *jar* container. The *typical use case* for such a situation is whenever you want to *export a library* which was *initially thought to work just for regular Java applications* but that now you would *also like to execute into an Android application*.\n\n\tOn the other hand, if you decide to **export an Android application** as a source for dynamic class loading, part of the upcoming procedure won't be necessary anymore. This happens because:\n\n\t\t1. When an *apk* container is generated, ``dx`` tool is automatically invoked. This means that by considering a valid *apk* container as a source for classes to load, the ``classes.dex`` entry will be already present and so you won't need to manually execute step *1* and step *2* of the following guide.\n\n\t\t2. Since Android requires an *apk* container to be signed to allow execution, you can decide, whenever you are ready to **export your application as a library**, to right click on the project and choose ``Android Tools -> Export Signed Application Package...``. By completing the wizard procedure, you are going to export a signed version of the final *apk* container and this basically covers the first *4* steps of the following guide.\n\nSo let us assume that you, as a library developer, want to export your project called \"MyLibrary\" into a *jar* archive compatible with ``SecureDexClassLoader``. The following steps should be performed:\n\n1. Export the project \"MyLibrary\" into a jar archive.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. highlight:: bash\n\nIf your project was developed using **Android Studio**, you can easily obtain a copy of your *jar* library by opening a terminal and pointing it to the main folder of your project and then by invoking a series of tasks through the ``./gradlew`` script as shown here::\n\n\t$ cd <absolute_path_to_your_jar_lib_project>\n\t$ ./gradlew clean build assembleRelease\n\nIf the build process goes smoothly, you should now be able to find a file presumably called *\"MyLibrary-release.jar\"* located under one of your project ``build/outputs`` folder. \n\n.. highlight:: java\n\nOn the other hand if you are relying on the **ADT (Android Development Tool)**, right-click on the project *\"MyLibrary\"* and select \"Export...\".\n\n.. image:: images/ExportJarOption.png\n\nThen choose the option \"Jar File\" and click \"Next...\".\n\n.. image:: images/ExportJarFile.png\n\nFinally choose the location of the exported *jar* archive by clicking on the \"Browse...\" button and then \"Finish\".\n\n.. image:: images/ExportJarFinish.png\n\nIndependently from which of the two methods you implied, you should have now successfully exported your project into a *jar* container!\n\n2. Translate Java Byte Code (.class) into Dalvik Byte Code (classes.dex).\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAfter having exported your project into a *jar* container you now have code that can run on a **Java Virtual Machine (JVM)** in the form of class file with the extensions ``.class``. Nevertheless in order to have your **code running** with ``SecureDexClassLoader`` **on an Android phone** it is necessary to **translate** the class files from Java Bytecode to **Dalvik Bytecode**. This task can be accomplished easily thanks to the ``dx`` tool, present in the Android SDK folder.\n\n.. note::\n\tNotice that **Dalvik Bytecode** is also compatible with the new `Android Runtime (ART) <https://source.android.com/devices/tech/dalvik/art.html>`_ system. This means that, except for narrow cases, you won't generally need to worry since your library code should execute fine on both the *Dalvik Virtual Machine (DVM)* and the *Android Runtime (ART)*. As related to this guide and more in general to *Grab'n Run*, *choosing one runtime system in stead of the other should not be an issue at all*.\n\n..\thighlight:: bash\n\nSo by assuming that you have just exported the project into a file called *myLibrary.jar* in a terminal type the following commands::\n\n$ cd <path_to_exported_jar>\n$ /<path_to_sdk>/build-tools/<last_stable_sdk_version>/dx --dex --output=myLibrary-dex.jar myLibrary.jar\n\nThe result is an output *jar* container called *myLibrary-dex.jar*. You can easily spot that no ``.class`` file is stored in this container and in stead a file called ``classes.dex`` was added. This is the direct **result of the translation** mentioned before. \n\n3. Generate a keypair and export the developer certificate\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf this is the first time that you sign a container you will need to **generate a key pair** with ``keytool`` and then **export a certificate** containing the newly created public key. Otherwise if you *already have a key pair and the associated certificate, simply skip this section* and continue reading from the next one.\n\nIn order to **generate a keystore and a key pair** type in the following command line in a terminal::\n\n$ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000  \n\nThis line prompts you for passwords for the keystore and private key, and to provide the Distinguished Name fields for your key. It then generates the keystore as a file called ``my-release-key.keystore``. The keystore will contain a single key, valid for 10000 days. The **alias** is a name that you choose to **identify keys** inside the keystore. In this case this private key will be identified as ``alias_name``.\n\nIf the previous step succeeded, now it is time to **export your developer certificate** that will be used by *application developers to verify your library code before dynamically loading it*. This can be accomplished again thanks to a ``keytool`` feature::\n\n$ keytool -exportcert -keystore my-release-key.keystore -alias alias_name -file certificate.pem\n\nThis command will export the certificate embedding the public key associated to the private key whose alias is ``alias_name``. This certificate will be stored in the file ``certificate.pem``.\n\nEven if the previous commands are all that you will need here, if you desire to deepen your knowledge on *keystore, keys and signing Android applications* visit these reference links:\n\n\t* https://www.digitalocean.com/community/tutorials/java-keytool-essentials-working-with-java-keystores\n\t* http://developer.android.com/tools/publishing/app-signing.html#signing-manually\n\n4. Sign the library with the developer private key.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNow it is time to **sign** the *jar* library with the **library developer private key** to enable the possibility to verify it.\n\nAssuming that you have generated a private key whose alias is ``alias_name`` and stored it in a keystore whose name is ``my-release-key.keystore`` in order to sign the *jar* container manually type in this line in your terminal::\n\n$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore myLibrary-dex.jar alias_name\n\nYou can then verify that the jar container is actually signed by typing::\n\n$ jarsigner -verify -verbose -certs myLibrary-dex.jar\n\t\n..\thighlight:: java\n\n.. note::\n\tWhen you verify the signature of the final container, you will receive a **warning message** like the following *\"This jar contains entries whose certificate chain is not validated\"*. This is absolutely normal since a **self-signed certificate** was used for the **verification process** and this is acceptable in Android as long as you are absolutely *sure that the certificate used for the verification is actually the library developer one*. In *Grab'n Run* the **chain of trust** is replaced by assuming that the certificate is stored on a domain which is directly controlled by the library developer and can only be retrieved via **HTTPS protocol**. \n\n5. Make the library and the certificate publicly available.\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe last step is **making public the signed version of the jar container**, obtained after the previous step, and the **exported certificate** embedding the library developer public key (*as explained in* `3. Generate a keypair and export the developer certificate`_ ).\n\nWhile you can *store the library container basically everywhere on the web* (application developers can retrieve your library via both HTTP or HTTPS protocol), it is **crucial and fundamental** for the whole security model to handle that you **publish your library developer certificate on a secure trustworthy remote location which can be accessed only via HTTPS protocol**.\n\nIf you have successfully followed up all the previous steps, you have now correctly **published your library** and application developers will be able to **run your code securely** by using ``SecureDexClassLoader``. \n\nLet GNR automatically handle library updates silently\n-----------------------------------------------------\n\nIn the end of this section **silent updating**, a *powerful feature* of **dynamic code loading**, is presented and easily and securely implemented with the use of *Grab'n Run*. Performing silent updates is a convenient techniques which can be used to **keep always updated third-party libraries or frameworks** by *decoupling the update process of the main application from those ones of the non-standalone libraries*. The **advantage** of such an approach is clearly the possibility to have always the **latest features and security workaround on third-party libraries** without continuously bothering the user on updating the application.\n\nDynamic code loading in this sense can be really effective in such a scenario since the latest version of the code can be retrieved from a remote URL just at runtime and then immediately executed.\n\nLet us now set up a possible use case for this technique and see how to implement it with Grab'n Run from both the library developer and the application developer side: imagine that an application developer wants to dynamically load the latest version of the already seen class ``com.example.ClassA`` stored in \"myLibrary-dex.jar\", a remote library.\n\nFrom the point of view of the **library developer** a couple of prerequisite steps must be performed:\n\n\t* The developer must prepare correctly a **signed version** of his/her library. For a complete walk-through on this task see the previous section `On library developer side: how to prepare a valid library container compatible with GNR`_.\n\t* Once that the last version of the library container is correctly prepared and signed, the **developer must publish** on a domain that (s)he controls a **redirect link** (i.e. ``http://mylibrary.it/downloads/mobile/latest``) which *points to the remote location where the library container is actually stored* (i.e. ``http://mylibrary.it/downloads/mobile/myLibrary-dex-1-8.jar``).\n\t* The developer must also set up a **secure link using HTTPS protocol**, which *points to the remote location of the certificate* associated to the private key used to sign the last version of the library (i.e. ``https://myLibrary.com/developerCert.pem``).\n\t* Every time that a **new version of the same library is ready** (i.e. version 1.9 of myLibrary is now available), the library developer will have to prepare the container in the usual way and sign it with the **SAME** private key associated to ``developerCert.pem`` and finally **update the redirect link** to *point to the location of the latest version of the container* (i.e. set up ``http://mylibrary.it/downloads/mobile/latest`` to redirect to ``http://mylibrary.it/downloads/mobile/myLibrary-dex-1-9.jar``).\n\n\t.. warning::\n\t\tWhile *Grab'n Run* **supports redirect links for the container remote location**, this kind of link is arbitrarily not accepted for remote certificates!!! This is a **security-oriented choice** since redirect links may jump from an HTTPS link to an HTTP one making the whole system insecure in case that the attacker performs a **Man-In-The-Middle-Attack** and substitute the proper certificate for the verification with a different one generated by himself. That is the reason why **redirect links for remote certificates will not be followed** by Grab'n Run and so no certificate file will be found for the container signature verification.\n\nOn the other hand the **application developer**, who wants to make use of the classes provided by ``myLibrary`` can easily accomplish this by setting up a ``SecureDexClassLoader`` where the *location pointing to the remote container* is the **redirect link** provided by the library developer and the **certificate** used for the verification is the *one stored at the secure URL on the library developer domain*. Here is a snippet of code that summarizes this operational description::\n\n\t\tClassA classAInstance = null;\n\t\t// The latest version of the library container is always found thanks to the redirect link\n\t\tjarContainerRemotePath = \"http://mylibrary.it/downloads/mobile/latest\";\n\n\t\ttry {\n\t\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\n\t\t\t// The package \"com.example\" is always signed by the library developer with \n\t\t\t// the same private key and so it can always be verified with the same \n\t\t\t// remote certificate.\n\t\t\tpackageNamesToCertMap.put(\t\"com.example\", \n\t\t\t\t\t\t\tnew URL(\"https://myLibrary.com/developerCert.pem\"));\n\n\t\t\t// The second parameter used here specifies how many days are counted before \n\t\t\t// a cached copy of the remote library container is considered rotten \n\t\t\t// and automatically discarded.\n\t\t\t// Default value is 5 days, here the value is lowered to 3..\n\t\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this, 3);\n\t\t\tSecureDexClassLoader mSecureDexClassLoader = \n\t\t\t\tmSecureLoaderFactory.createDexClassLoader(\tjarContainerRemotePath, \n\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\tpackageNamesToCertMap, \n\t\t\t\t\t\t\t\t\t\tgetClass().getClassLoader());\n\t\t\n\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(\"com.example.ClassA\");\n\n\t\t\t// Check whether the signature verification process succeeds\n\t\t\tif (loadedClass != null) {\n\n\t\t\t\t// Class loading was successful and performed in a safe way.\n\t\t\t\t// The last version of ClassA has been successfully retrieved!\n\t\t\t\tclassAInstance = (ClassA) loadedClass.newInstance();\n\t\t\t\t\n\t\t\t\t// Do something with the loaded object classAInstance\n\t\t\t\t// i.e. classAInstance.doSomething();\n\t\t\t}\n\n\t\t} catch (ClassNotFoundException e) {\n\t\t\t// This exception will be raised when the container of the target class\n\t\t\t// is genuine but this class file is missing..\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (MalformedURLException e) {\n\t\t\t// The previous URL used for the packageNamesToCertMap entry was a malformed one.\n\t\t\tLog.e(\"Error\", \"A malformed URL was provided for a remote certificate location\");\n\t\t} \n\n.. Library developer side:\n\n..\t* Read previous section.\n..\t* Use a redirect HTTP link to point to the last version of the signed jar library container\n..\t* Use an HTTPS link to make the certificate for verification public\n\n.. Application developer side:\n..\t* Initialize SecureDexClassLoader with dexPath pointing to the redirect HTTP link for the updated container and associate the package name to the remote URL of the library developer certificate\n\n.. DONE :)\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# docgrabnrun documentation build configuration file, created by\n# sphinx-quickstart on Tue Sep 23 09:59:33 2014.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys\nimport os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\nsys.path.insert(0, os.path.abspath('../../grab-n-run/gnr/src/'))\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.doctest',\n    #'javasphinx',\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'docgrabnrun'\ncopyright = u'2014, Luca Falsina'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '1.0'\n# The full version, including alpha/beta/rc tags.\nrelease = '1.0'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = ['_build']\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The language used in code block to set keywords highlight.\nhighlight_language = 'java'\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n#keep_warnings = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'default'\n# Haiku theme\n# html_theme = 'haiku'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = []\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n#html_extra_path = []\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'docgrabnrundoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n# The paper size ('letterpaper' or 'a4paper').\n#'papersize': 'letterpaper',\n\n# The font size ('10pt', '11pt' or '12pt').\n#'pointsize': '10pt',\n\n# Additional stuff for the LaTeX preamble.\n#'preamble': '',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n  ('index', 'docgrabnrun.tex', u'docgrabnrun Documentation',\n   u'Luca Falsina', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'docgrabnrun', u'docgrabnrun Documentation',\n     [u'Luca Falsina'], 1)\n]\n\n# If true, show URL addresses after external links.\n#man_show_urls = False\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n  ('index', 'docgrabnrun', u'docgrabnrun Documentation',\n   u'Luca Falsina', 'docgrabnrun', 'One line description of project.',\n   'Miscellaneous'),\n]\n\n# Documents to append as an appendix to all manuals.\n#texinfo_appendices = []\n\n# If false, no module index is generated.\n#texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#texinfo_show_urls = 'footnote'\n\n# If true, do not generate a @detailmenu in the \"Top\" node's menu.\n#texinfo_no_detailmenu = False\n"
  },
  {
    "path": "docs/example.rst",
    "content": "Discussion of an example project\n================================\n\nBefore digging into this section, you are **strongly** encouraged to read :doc:`tutorial` for an **introductory description** on the features of Grab`n Run library.\n\nThe **aim of the sample application** is to give you some *hints on how to use the classes in Grab'n Run and how they will behave across different contexts*. The **source code** of the example can be found in the ``example`` folder of *Grab'n Run* repository.\n\nDifferent extracts of code will be considered and explained in the following sections of this page but before analyzing the code it may be convenient to retrieve it and to set up an **already prepared Android smart phone emulator** that contains all the containers needed to run the example code..\n\nRetrieve the example code and the emulator\n------------------------------------------\n\nRetrieve Grab'n Run full repository\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nAt first you will need to recover *Grab'n Run* example code. In order to do so you need to have **Git** installed on your machine.\nThe latest version can be found at Git download `page <http://git-scm.com/downloads>`_.\n\n..\thighlight:: bash\n\nNext open a terminal and **clone** the example repository into ``grab-n-run``, a local folder located at ``absolute_path_to_gnr_repo``, through Git::\n\n\t$ cd <absolute_path_to_gnr_repo>\n\t$ mkdir grab-n-run\n\t$ cd grab-n-run\n\t$ git clone \"https://github.com/lukeFalsina/Grab-n-Run.git\"\n\n..\thighlight:: java\n\nAt the end of the process you will have all the GNR code locally including a copy of the *example application* and of the *documentation*.\n\nInclude Grab'n Run example code in your IDE \n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe next step is *importing the example sources* into an **IDE**. The process will be now described for both **Android Studio (AS)** and **Android Development Tool (ADT)**.\n\na. **Android Studio (AS)**\n\nIn the welcome window of Android Studio select from the *Quick Start* menu the option \"Open an existing Android Studio project\".\n\n.. image:: images/ASImportGNR1.png\n\nNext navigate to the ``grab-n-run`` folder in which you previously cloned the repository and then pick the project ``AS`` from the ``example`` subfolder as shown in the image below.\n\n.. image:: images/ASImportGNR2.png\n\nThe example project should have been now successfully imported! It may be necessary to rebulit it again by picking the option \"Make Project\".\n\n.. image:: images/ASImportGNR3.png\n\n**P.S.** Notice that you can open in *Android Studio* also the **original GNR library project** by using the very same procedure but by picking the ``gnr`` Studio project from the main ``grab-n-run`` folder in stead of the ``example/AS`` one.\n\n.. image:: images/ASImportGNR4.png\n\nb. **Android Development Tool (ADT)**\n\nAt first right click in the *Package Explorer* and select \"Import..\"\n\n.. image:: images/ADTImportGNR1.png\n\nNext select under the *Android* folder \"Existing Android Code Into Workspace\" and then \"Next >\"\n\n.. image:: images/ADTImportGNR2.png\n\nBy pressing the \"Browse...\" button point the *Root Directory* to the ``grab-n-run`` folder in which you previously cloned the repository and then to the subfolder ``grab-n-run/example/ADT``. You should be now able to the see and select the candidate project ``ExampleAppGNR`` (*an example application which makes use of GNR*). In the end press \"Finish\" to import the example project. Below you can see a screenshot which summarizes all the settings before the \"Finish\" button is clicked.\n\n.. image:: images/ADTImportGNR3.png\n\nAt the end of this process you should have been able to **correctly import** the example application!\n\n.. image:: images/ADTImportGNR4.png\n\nRetrieve and set up the emulator\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n..\thighlight:: bash\n\nThen it is time to retrieve the **emulator** used to run the example application. You can easily find it in the ``assets`` folder of the ``example`` repository.\nSo once that you have located the compressed file ``ExampleAppGNREmu.tar.gz`` containing the emulator, open a terminal and at first copy this file into your *home* folder::\n\n\t$ cd <absolute_path_to_gnr_repo>/example/\n\t$ cp ExampleAppGNREmu.tar.gz ~\n\nNext decompress this container:: \n\n\t$ cd ~\n\t$ tar xzf ExampleAppGNREmu.tar.gz\n\nThis operation will generate two files, a folder called ``ExampleAppGNREmu.avd`` and a configuration file ``ExampleAppGNREmu.ini``. In the end move these two files into the Android emulator folder, normally located at ``/home/<your_username>/.android/avd``::\n\n\t$ mv ExampleAppGNREmu.avd ExampleAppGNREmu.ini .android/avd\n\nThe last step consist in editing the ``path`` variable stored in the configuration file. So open ``ExampleAppGNREmu.ini`` at the final location with a text editor and change the path variable in order to match the current location of the ``ExampleAppGNREmu.avd`` folder. So if my user name is for example *bill90*, I need to change the path variable from ``path=/home/<USER_NAME>/.android/avd/ExampleAppGNREmu.avd`` to ``path=/home/bill90/.android/avd/ExampleAppGNREmu.avd``. \n\n..\thighlight:: java\n\nBefore starting the emulator in your **IDE**, remember to **verify that the SDK version 17** is installed on your machine since the emulator targets that version. Otherwise you can *also edit the emulator configuration* from your IDE to target a different and **more recent** version of the SDK which is installed on your machine.\n\n.. note::\n\tAndroid emulator is unfortunately pretty slow and requires also a big bunch of resources and that is the reason why it may be not supported by different machines. A couple of empirical suggestions in this direction are the following:\n\n\t* If possible, try to target directly **SDK version 17**, as it results to me that the more recent SDK version you target, the more time the emulator requires before setting up.\n\t* It is a really good idea to enable the **snapshot feature**. This lets the system frame the current situation of the emulator when you turn it off and load it back whenever you restart the emulator with a *significant reduction of the waiting time*. This `post <http://stackoverflow.com/questions/1554099/why-is-the-android-emulator-so-slow>`_ explains how to enable the feature.\n\t* Emulator can be switched between landscape and portrait view by pressing ``ctrl + F12``. This can be useful to interact properly with the example application.\n\nWhen the emulator is finally set up, you can start it in either **ADT Eclipse** or **Android Studio** (it may take time depending on your machine..). Next, whenever you want to run the example code and the IDE asks which device should be used, remember to **select this emulator as the running Android device**.\n\nIn case you need to integrate this previous concise walk-through, please give a look at these other resources:\n\t\n\t* https://blahti.wordpress.com/2011/08/24/how-to-export-and-import-android-virtual-device-avd-files/\n\t* http://stackoverflow.com/questions/4575167/android-how-to-copy-the-emulator-to-a-friend-for-testing\n\nList of example containers\n--------------------------\n\nIn order to understand correctly the following detailed discussion, it is fundamental to first introduce the containers (*jar* and *apk* archives), retrieved for the code loading in the example code. Here is a list of the string variables that store the path to various containers:\n\n* ``exampleSignedAPKPath``: URI of a **benign** toy *apk* container signed with a valid *developer certificate*.\n* ``exampleTestAPKPath``: path location pointing to the same **benign** *apk* container but this time signed with the *Android Debug Certificate*. \n* ``exampleSignedChangedAPKPath``: URI pointing to a **handled version** of the same container stored at ``exampleSignedAPKPath`` in which a part of the signatures has been modified.\n* ``jarContainerPath``: path location to the **benign** *jar* container used to customize the view elements inside an example activity.\n* ``jarContainerRepackPath``: URI pointing to a **malicious repackaged** version of the original container stored at ``jarContainerPath``.\n\nMainActivity.java\n-----------------\n\n`MainActivity <https://github.com/lukeFalsina/Grab-n-Run/blob/master/example/src/it/polimi/poccodeloading/MainActivity.java>`_ is the **entry point** of the sample application. In its overloaded method ``onCreate()`` it initializes through a ``ListView`` a set of buttons used to select the *different test cases* present in the application.\n\nDexClassLoader (apk) vs SecureDexClassLoader (apk)\n----------------------------------------------------\n\nIn this first scenario you will consider how to retrieve an `Activity <http://developer.android.com/reference/android/app/Activity.html>`_ class, whose name is ``NasaDailyImage``, stored in the *apk* container, called *test.apk*, through the use of `DexClassLoader <http://developer.android.com/reference/dalvik/system/DexClassLoader.html>`_ and ``SecureDexClassLoader``.\n\nThe relevant **code** in this case is the one of the two methods ``setUpDexClassLoader()`` and ``setUpSecureDexClassLoader()``, which are triggered by tapping the related two buttons on the ``MainActivity`` view.\n\nsetUpDexClassLoader()\n~~~~~~~~~~~~~~~~~~~~~\n\nIn this method a standard initialization of a ``DexClassLoader`` is applied.\nSo at first the usual **application-private, writable directory** for caching loaded *.dex* classes must be set up.\n\nThen a ``DexClassLoader`` object is initialized using *test.apk*, a container located directly in the phone external storage ( as described by ``exampleTestAPKPath``), as its *jar path* for the classes to load.\n\nFinally the ``NasaDailyImage`` Activity is loaded. If such an operation is successful the **simple name** of the **loaded class** is shown to the user through a *toast message*; otherwise different **exceptions** are raised and show again through a toast message an appropriate helper message.\n\nsetUpSecureDexClassLoader()\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn this method **repeated** ``loadClass()`` **calls** are performed on differently initialized ``SecureDexClassLoader`` instances in order to *show different behaviors* of the loader class while retrieving the usual ``NasaDailyImage`` Activity.\n\nAt first a ``SecureLoaderFactory`` object is created. Then this instance is used to generate three ``SecureDexClassLoader`` that covers different cases and ends up with different results on the load operation:\n\n1.\t**Test case 1:** Load a class through ``SecureDexClassLoader`` without providing an associative map for certificates location\n\n\tThis first test case shows a **possible error** that a developer may encounter when using this library for the first time.\n\tIf you want to have the location of the certificate being computed by reversing the package name, as explained in :ref:`Reverse package name to obtain remote certificate URL`, you still need to **populate an associative map** with entries like (*\"any.package.name\"*, **null**) and use it as a parameter of the method ``createDexClassLoader()``. To understand why the class works in this way think of this system as a kind of `white listing <http://en.wikipedia.org/wiki/Whitelist>`_. Only those classes inside package names which are *declared into the associative map* or *directly descend* from one of the declared package names will be considered as possible valid ones, while all classes belonging to a **not listed package name or not a descendant of the declared ones** will be **immediately rejected**.\n\n\tAnd this is exactly what happens in this test case where **no associative map is provided** and so all the classes in the two containers, including the target ``NasaDailyImage``, are **prevented from being loaded** since there is *no clue on the certificate location*.\n\n2.\t**Test case 2:** Unsuccessful load of a class through ``SecureDexClassLoader`` with an associative map *(Debug certificate)*\n\n\tIn the second test case you can see different ways to **populate** the associative map ``packageNamesToCertMap``, used to *link packages with certificates location*.\n\n\t.. warning::\n\t\tAlways keep in mind that **prior** to **downloading** a certificate from the **web** the certificate for that package will be **searched inside the application-private directory** reserved for certificates and then possibly at the remote location. If you wish to *just look at the remote URL* without considering cached certificates, always remember to **wipe out private application data** through the invocation of the method ``wipeOutPrivateAppCachedData()`` **before dismissing** your ``SecureDexClassLoader`` instances. In such a way every time that a new ``SecureDexClassLoader`` is created, you will be sure that no cached resource will be associated with it.\n\n\n\tThe first ``put()`` *call* inserts the package name *headfirstlab.nasadailyimage* of the class that we would like to load later in the example and associates it with a **valid remote URL**. What you can immediately notice by pointing your browser to that URL is that the *remote certificate* in this case is a **self-signed developer** one since the **subject** of the certificate is **also** the **issuer** of it but, as it is mentioned in the :doc:`tutorial`, this is perfectly fine in the **Android** environment.\n\n\tThe *second entry* inserted into the associative map provides a *remote URL* to an **inexistent certificate** (once again you can try to point there your browser to easy spot this out). More over since *no certificate for the package name ``it.polimi.example`` has been already cached into the application-private certificate directory*, then **no certificate** is **available** for it and that is the reason why *any class* belonging to the ``it.polimi.example`` package will be **rejected and prevented from being loaded** by ``SecureDexClassLoader``.\n\n\tLastly the third ``put()`` call on the associative map will insert a package name that will be also used to *construct the remote certificate URL* (**reverse package name**). Once again the final remote URL (``https://polimi.it/example3/certificate.pem``) points to no certificate so any class, whose package name is *it.polimi.example3*, will be rejected from being loaded.\n\n\tIn the end a ``SecureDexClassLoader`` is generated using as a container file a valid *apk* containing the target class but **signed with a certificate**, the *Debug Android Certificate*, which is different from the one issued by the developer. For such a reason the result of the ``loadClass()`` method will be that *no class object is going to be returned* since the apk is **not signed** with the **required certificate**.\n\n3.\t**Test case 3:** Unsuccessful load of a class through ``SecureDexClassLoader`` with an associative map *(Failed signatures verification of some container's entries)*\n\n\tIn the third test case you can immediately notice that all the settings for the invocation of ``SecureDexClassLoader`` are equals to those of the previous case except for the chosen *apk* container. In fact, while before the container was signed with a non valid certificate, this time the container is signed with the **right certificate** but someone **modified** a couple of the **entries signature**, which do not match anymore with the one obtained during the signing procedure. To sum up also in this case *no class will be loaded* since this container results to be **partially corrupted** and so not safe.\n\n4.\t**Test case 4:** Successful load of a class through ``SecureDexClassLoader`` with an associative map\n\n\tIn this last test case a **successful example** of dynamic code loading is shown. This time ``SecureDexClassLoader`` is initialized with a **valid** *apk* container, **signed** with the **correct developer certificate**, and with the associative map previously initialized in *Test case 2*. The whole process works fine since this associative map contains the necessary key entry *headfirstlab.nasadailyimage* and the related developer **certificate** has been **already cached** during *Test case 2*. Finally during the **signature verification step** inside the ``loadClass()`` method all the entries inside the container match properly with their signature and the certificate used for that signing process is exactly the one linked to *headfirstlab.nasadailyimage* package. That is the reason why *dynamic loading* of ``NasaDailyImage`` activity is **allowed**.\n\nDexClassLoader (jar) vs SecureDexClassLoader (jar)\n----------------------------------------------------\n\nA different scenario to show the power of *dynamic code* loading and the **security weakness** of the standard ``DexClassLoader`` is represented by the following example. In this case another activity (the source code is contained into *DexClassSampleActivity.java*) instantiates a certain number of **GUI components** (a couple of buttons, a text view, a switch..) and then **customizes** them according to the methods of an object belonging to the **external** class ``ComponentModifier``, which is **dynamically loaded** at run time.\n\nDepending on the user choice (tapping the first button in stead of the second one) a different extension class of ``ComponentModifier`` is loaded and a different behavior is shown to the user even if the static code shown in ``DexClassSampleActivity`` is exactly the same (as you can easily check by inspecting the method ``onBtnClick()``). This loading operation can be realized easily by means of ``DexClassLoader`` as shown in the method ``retrieveComponentModifier()`` of the source code..\n\nThat's just a pity that the container used to load dynamically the class by ``DexClassLoader`` in this example is actually *randomly selected at run time* between either a benign version or a **repackaged one** of the original *apk* and so **malicious code** could potentially have been **executed** *without the user even notice it*!\n\nBut let's explain how this could possibly happen: in ``DexClassSampleActivity`` there is a simple private method called ``randomContainerPathChoice()``, which in this case is invoked before the instantiation of both ``DexClassLoader`` and ``SecureDexClassLoader`` and which **select randomly the path** of either the **benign** version of the ``ComponentModifier`` container, stored in the string ``jarContainerPath``, or the path of the **repackaged** one with the string ``jarContainerRepackPath``.\n\n``DexClassLoader`` *won't notice and care* about this difference as long as in both the containers there is an implementation of the required **target class** to load and that is the reason why repeating tapping on the first button ''Click me!'' in the Activity screen multiple times will end up in executing two different version of the same ``FirstComponentModifierImpl`` class. \n\nOn the other hand if you perform the same experiment with ``SecureDexClassLoader`` the repackaged *apk* container choice this time will be detected and blocked during the **signature verification procedure** against the developer certificate in the ``loadClass()`` method. This is possible since *malicious modified entries will not succeed in the signature verification check computed by considering both the initial signature stored inside the container and the developer certificate* retrieved from the associative map used to initialize the ``SecureDexClassLoader`` instance. Thanks to this, ``SecureDexClassLoader`` **won't load** the customization classes inside the *repackaged container* and it will just **end up the activity**, which is exactly the **secure** behavior that you, *as a developer*, would like to obtain :)  \n\n.. Create PackageContext\n.. ---------------------\n\n.. Coming soon.. More or less ;)"
  },
  {
    "path": "docs/index.rst",
    "content": ".. docgrabnrun documentation master file, created by\n   sphinx-quickstart on Tue Sep 23 09:59:33 2014.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nWelcome to Grab'n Run documentation!\n====================================\n\n*Grab'n Run* (aka **GNR**) is a **simple** and **effective** Java Library that you can add to your Android projects to secure *dynamic class loading* operations.\n\nFor a **quick start** on how to include the library and how to use it in your projects give a look at :doc:`tutorial`.\n\nFor a brief explanation on the **issue** of *insecure dynamic class loading* and on Grab'n Run purpose check the :doc:`security` section.\n\nA concise **example** of use of the library is provided into an Android toy-application `here <https://github.com/lukeFalsina/Grab-n-Run/tree/master/example>`_. A *full explanation* of key extracts of this code is given into the :doc:`example` section.\n\nFor a description on Grab'n Run **API** in *JavaDoc* style please refer to the `API documentation <https://rawgit.com/lukeFalsina/Grab-n-Run/master/docs/javaDoc/index.html>`_. \n\nFor those willing for more **technicalities** and **advanced features** implemented in *Grab'n Run*, the section on :doc:`complementary` is a *must-read*. This part of the documentation can also be used for **reference** as it presents how to handle properly some **tricky situations** that may occur while using *GNR*.\n\nFor an introduction on how to use the POC script for rewriting your application automatically to use the secure Grab'n Run API instead of the regualr ones for dynamic \ncode loading, check out the :doc:`repackaging` section.\n\n.. toctree::\n   :maxdepth: 2\n\n   tutorial\n   security\n   example\n   complementary\n   repackaging\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n\n"
  },
  {
    "path": "docs/javaDoc/allclasses-frame.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>All Classes</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<h1 class=\"bar\">All Classes</h1>\n<div class=\"indexContainer\">\n<ul>\n<li><a href=\"it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\" target=\"classFrame\">SecureDexClassLoader</a></li>\n<li><a href=\"it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\" target=\"classFrame\">SecureLoaderFactory</a></li>\n</ul>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/allclasses-noframe.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>All Classes</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<h1 class=\"bar\">All Classes</h1>\n<div class=\"indexContainer\">\n<ul>\n<li><a href=\"it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></li>\n<li><a href=\"it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></li>\n</ul>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/constant-values.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Constant Field Values</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"Constant Field Values\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"overview-tree.html\">Tree</a></li>\n<li><a href=\"deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li><a href=\"help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?constant-values.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"constant-values.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 title=\"Constant Field Values\" class=\"title\">Constant Field Values</h1>\n<h2 title=\"Contents\">Contents</h2>\n<ul>\n<li><a href=\"#it.necst\">it.necst.*</a></li>\n</ul>\n</div>\n<div class=\"constantValuesContainer\"><a name=\"it.necst\">\n<!--   -->\n</a>\n<h2 title=\"it.necst\">it.necst.*</h2>\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Constant Field Values table, listing constant fields, and values\">\n<caption><span>it.necst.grabnrun.<a href=\"it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colFirst\" scope=\"col\">Modifier and Type</th>\n<th scope=\"col\">Constant Field</th>\n<th class=\"colLast\" scope=\"col\">Value</th>\n</tr>\n<tbody>\n<tr class=\"altColor\">\n<td class=\"colFirst\"><a name=\"it.necst.grabnrun.SecureLoaderFactory.DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\">\n<!--   -->\n</a><code>public&nbsp;static&nbsp;final&nbsp;int</code></td>\n<td><code><a href=\"it/necst/grabnrun/SecureLoaderFactory.html#DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\">DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY</a></code></td>\n<td class=\"colLast\"><code>5</code></td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"overview-tree.html\">Tree</a></li>\n<li><a href=\"deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li><a href=\"help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?constant-values.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"constant-values.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/deprecated-list.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Deprecated List</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"Deprecated List\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"overview-tree.html\">Tree</a></li>\n<li class=\"navBarCell1Rev\">Deprecated</li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li><a href=\"help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?deprecated-list.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"deprecated-list.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 title=\"Deprecated API\" class=\"title\">Deprecated API</h1>\n<h2 title=\"Contents\">Contents</h2>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"overview-tree.html\">Tree</a></li>\n<li class=\"navBarCell1Rev\">Deprecated</li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li><a href=\"help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?deprecated-list.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"deprecated-list.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/help-doc.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>API Help</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"API Help\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"overview-tree.html\">Tree</a></li>\n<li><a href=\"deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li class=\"navBarCell1Rev\">Help</li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?help-doc.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"help-doc.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 class=\"title\">How This API Document Is Organized</h1>\n<div class=\"subTitle\">This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.</div>\n</div>\n<div class=\"contentContainer\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<h2>Package</h2>\n<p>Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:</p>\n<ul>\n<li>Interfaces (italic)</li>\n<li>Classes</li>\n<li>Enums</li>\n<li>Exceptions</li>\n<li>Errors</li>\n<li>Annotation Types</li>\n</ul>\n</li>\n<li class=\"blockList\">\n<h2>Class/Interface</h2>\n<p>Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:</p>\n<ul>\n<li>Class inheritance diagram</li>\n<li>Direct Subclasses</li>\n<li>All Known Subinterfaces</li>\n<li>All Known Implementing Classes</li>\n<li>Class/interface declaration</li>\n<li>Class/interface description</li>\n</ul>\n<ul>\n<li>Nested Class Summary</li>\n<li>Field Summary</li>\n<li>Constructor Summary</li>\n<li>Method Summary</li>\n</ul>\n<ul>\n<li>Field Detail</li>\n<li>Constructor Detail</li>\n<li>Method Detail</li>\n</ul>\n<p>Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.</p>\n</li>\n<li class=\"blockList\">\n<h2>Annotation Type</h2>\n<p>Each annotation type has its own separate page with the following sections:</p>\n<ul>\n<li>Annotation Type declaration</li>\n<li>Annotation Type description</li>\n<li>Required Element Summary</li>\n<li>Optional Element Summary</li>\n<li>Element Detail</li>\n</ul>\n</li>\n<li class=\"blockList\">\n<h2>Enum</h2>\n<p>Each enum has its own separate page with the following sections:</p>\n<ul>\n<li>Enum declaration</li>\n<li>Enum description</li>\n<li>Enum Constant Summary</li>\n<li>Enum Constant Detail</li>\n</ul>\n</li>\n<li class=\"blockList\">\n<h2>Use</h2>\n<p>Each documented package, class and interface has its own Use page.  This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A.  You can access this page by first going to the package, class or interface, then clicking on the \"Use\" link in the navigation bar.</p>\n</li>\n<li class=\"blockList\">\n<h2>Tree (Class Hierarchy)</h2>\n<p>There is a <a href=\"overview-tree.html\">Class Hierarchy</a> page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with <code>java.lang.Object</code>. The interfaces do not inherit from <code>java.lang.Object</code>.</p>\n<ul>\n<li>When viewing the Overview page, clicking on \"Tree\" displays the hierarchy for all packages.</li>\n<li>When viewing a particular package, class or interface page, clicking \"Tree\" displays the hierarchy for only that package.</li>\n</ul>\n</li>\n<li class=\"blockList\">\n<h2>Deprecated API</h2>\n<p>The <a href=\"deprecated-list.html\">Deprecated API</a> page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.</p>\n</li>\n<li class=\"blockList\">\n<h2>Index</h2>\n<p>The <a href=\"index-files/index-1.html\">Index</a> contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.</p>\n</li>\n<li class=\"blockList\">\n<h2>Prev/Next</h2>\n<p>These links take you to the next or previous class, interface, package, or related page.</p>\n</li>\n<li class=\"blockList\">\n<h2>Frames/No Frames</h2>\n<p>These links show and hide the HTML frames.  All pages are available with or without frames.</p>\n</li>\n<li class=\"blockList\">\n<h2>All Classes</h2>\n<p>The <a href=\"allclasses-noframe.html\">All Classes</a> link shows all classes and interfaces except non-static nested types.</p>\n</li>\n<li class=\"blockList\">\n<h2>Serialized Form</h2>\n<p>Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking \"Serialized Form\" in the \"See also\" section of the class description.</p>\n</li>\n<li class=\"blockList\">\n<h2>Constant Field Values</h2>\n<p>The <a href=\"constant-values.html\">Constant Field Values</a> page lists the static final fields and their values.</p>\n</li>\n</ul>\n<em>This help file applies to API documentation generated using the standard doclet.</em></div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"overview-tree.html\">Tree</a></li>\n<li><a href=\"deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li class=\"navBarCell1Rev\">Help</li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?help-doc.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"help-doc.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index-files/index-1.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>C-Index</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"C-Index\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev Letter</li>\n<li><a href=\"index-2.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-1.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-1.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"contentContainer\"><a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;<a name=\"_C_\">\n<!--   -->\n</a>\n<h2 class=\"title\">C</h2>\n<dl>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\">createDexClassLoader(String, String, ClassLoader, Map&lt;String, URL&gt;)</a></span> - Method in class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></dt>\n<dd>\n<div class=\"block\">Creates a <a href=\"../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> that finds interpreted and native code in a set of\n provided locations (either local or remote via HTTP or HTTPS) in dexPath.</div>\n</dd>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map,%20boolean)\">createDexClassLoader(String, String, ClassLoader, Map&lt;String, URL&gt;, boolean)</a></span> - Method in class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></dt>\n<dd>\n<div class=\"block\">This method returns a <a href=\"../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> instance in the same way as it is \n explained in the <a href=\"../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\"><code>createDexClassLoader(String, String, ClassLoader, Map)</code></a>.</div>\n</dd>\n</dl>\n<a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev Letter</li>\n<li><a href=\"index-2.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-1.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-1.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index-files/index-2.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>D-Index</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"D-Index\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-1.html\">Prev Letter</a></li>\n<li><a href=\"index-3.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-2.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-2.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"contentContainer\"><a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;<a name=\"_D_\">\n<!--   -->\n</a>\n<h2 class=\"title\">D</h2>\n<dl>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureLoaderFactory.html#DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\">DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY</a></span> - Static variable in class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></dt>\n<dd>\n<div class=\"block\">When a remote container URL is encountered, this field specifies the default time interval, \n expressed in days, before which a local copy of a remote container, stored in an \n application-private directory, will be considered fresh and so acceptable to be cached.</div>\n</dd>\n</dl>\n<a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-1.html\">Prev Letter</a></li>\n<li><a href=\"index-3.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-2.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-2.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index-files/index-3.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>I-Index</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"I-Index\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-2.html\">Prev Letter</a></li>\n<li><a href=\"index-4.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-3.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-3.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"contentContainer\"><a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;<a name=\"_I_\">\n<!--   -->\n</a>\n<h2 class=\"title\">I</h2>\n<dl>\n<dt><a href=\"../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a> - package it.necst.grabnrun</dt>\n<dd>&nbsp;</dd>\n</dl>\n<a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-2.html\">Prev Letter</a></li>\n<li><a href=\"index-4.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-3.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-3.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index-files/index-4.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>L-Index</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"L-Index\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-3.html\">Prev Letter</a></li>\n<li><a href=\"index-5.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-4.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-4.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"contentContainer\"><a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;<a name=\"_L_\">\n<!--   -->\n</a>\n<h2 class=\"title\">L</h2>\n<dl>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureDexClassLoader.html#loadClass(java.lang.String)\">loadClass(String)</a></span> - Method in class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></dt>\n<dd>&nbsp;</dd>\n</dl>\n<a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-3.html\">Prev Letter</a></li>\n<li><a href=\"index-5.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-4.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-4.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index-files/index-5.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>S-Index</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"S-Index\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-4.html\">Prev Letter</a></li>\n<li><a href=\"index-6.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-5.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-5.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"contentContainer\"><a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;<a name=\"_S_\">\n<!--   -->\n</a>\n<h2 class=\"title\">S</h2>\n<dl>\n<dt><a href=\"../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">SecureDexClassLoader</span></a> - Class in <a href=\"../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a></dt>\n<dd>\n<div class=\"block\">A class that provides an extension of default <a href=\"http://d.android.com/reference/dalvik/system/DexClassLoader.html?is-external=true\" title=\"class or interface in dalvik.system\"><code>DexClassLoader</code></a> \n provided by the Android system and it is used to load classes \n from jar and apk container files including a classes.dex entry in a secure way.</div>\n</dd>\n<dt><a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">SecureLoaderFactory</span></a> - Class in <a href=\"../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a></dt>\n<dd>\n<div class=\"block\">A Factory class that generates instances of classes used to \n retrieve dynamic code in a secure way at run time.</div>\n</dd>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureLoaderFactory.html#SecureLoaderFactory(ContextWrapper)\">SecureLoaderFactory(ContextWrapper)</a></span> - Constructor for class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></dt>\n<dd>\n<div class=\"block\">Creates a <a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><code>SecureLoaderFactory</code></a> used to check and generate instances \n from secure dynamic code loader classes.</div>\n</dd>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureLoaderFactory.html#SecureLoaderFactory(ContextWrapper,%20int)\">SecureLoaderFactory(ContextWrapper, int)</a></span> - Constructor for class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></dt>\n<dd>\n<div class=\"block\">This constructor works exactly as the previous one but it also allows to \n decide the time interval in days before which a local copy of a remote container, stored in an \n application-private directory, will be considered fresh and so acceptable to be cached.</div>\n</dd>\n</dl>\n<a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-4.html\">Prev Letter</a></li>\n<li><a href=\"index-6.html\">Next Letter</a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-5.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-5.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index-files/index-6.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>W-Index</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"W-Index\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-5.html\">Prev Letter</a></li>\n<li>Next Letter</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-6.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-6.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"contentContainer\"><a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;<a name=\"_W_\">\n<!--   -->\n</a>\n<h2 class=\"title\">W</h2>\n<dl>\n<dt><span class=\"strong\"><a href=\"../it/necst/grabnrun/SecureDexClassLoader.html#wipeOutPrivateAppCachedData(boolean,%20boolean)\">wipeOutPrivateAppCachedData(boolean, boolean)</a></span> - Method in class it.necst.grabnrun.<a href=\"../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></dt>\n<dd>\n<div class=\"block\">Sometimes it may be useful to remove those data that have been cached in \n the private application folders (basically for performance reason or for making \n <a href=\"../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> works also partially offline).</div>\n</dd>\n</dl>\n<a href=\"index-1.html\">C</a>&nbsp;<a href=\"index-2.html\">D</a>&nbsp;<a href=\"index-3.html\">I</a>&nbsp;<a href=\"index-4.html\">L</a>&nbsp;<a href=\"index-5.html\">S</a>&nbsp;<a href=\"index-6.html\">W</a>&nbsp;</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li><a href=\"../overview-tree.html\">Tree</a></li>\n<li><a href=\"../deprecated-list.html\">Deprecated</a></li>\n<li class=\"navBarCell1Rev\">Index</li>\n<li><a href=\"../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"index-5.html\">Prev Letter</a></li>\n<li>Next Letter</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../index.html?index-filesindex-6.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"index-6.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/index.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Frameset//EN\" \"http://www.w3.org/TR/html4/frameset.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Generated Documentation (Untitled)</title>\n<script type=\"text/javascript\">\n    targetPage = \"\" + window.location.search;\n    if (targetPage != \"\" && targetPage != \"undefined\")\n        targetPage = targetPage.substring(1);\n    if (targetPage.indexOf(\":\") != -1 || (targetPage != \"\" && !validURL(targetPage)))\n        targetPage = \"undefined\";\n    function validURL(url) {\n        try {\n            url = decodeURIComponent(url);\n        }\n        catch (error) {\n            return false;\n        }\n        var pos = url.indexOf(\".html\");\n        if (pos == -1 || pos != url.length - 5)\n            return false;\n        var allowNumber = false;\n        var allowSep = false;\n        var seenDot = false;\n        for (var i = 0; i < url.length - 5; i++) {\n            var ch = url.charAt(i);\n            if ('a' <= ch && ch <= 'z' ||\n                    'A' <= ch && ch <= 'Z' ||\n                    ch == '$' ||\n                    ch == '_' ||\n                    ch.charCodeAt(0) > 127) {\n                allowNumber = true;\n                allowSep = true;\n            } else if ('0' <= ch && ch <= '9'\n                    || ch == '-') {\n                if (!allowNumber)\n                     return false;\n            } else if (ch == '/' || ch == '.') {\n                if (!allowSep)\n                    return false;\n                allowNumber = false;\n                allowSep = false;\n                if (ch == '.')\n                     seenDot = true;\n                if (ch == '/' && seenDot)\n                     return false;\n            } else {\n                return false;\n            }\n        }\n        return true;\n    }\n    function loadFrames() {\n        if (targetPage != \"\" && targetPage != \"undefined\")\n             top.classFrame.location = top.targetPage;\n    }\n</script>\n</head>\n<frameset cols=\"20%,80%\" title=\"Documentation frame\" onload=\"top.loadFrames()\">\n<frame src=\"allclasses-frame.html\" name=\"packageFrame\" title=\"All classes and interfaces (except non-static nested types)\">\n<frame src=\"it/necst/grabnrun/package-summary.html\" name=\"classFrame\" title=\"Package, class and interface descriptions\" scrolling=\"yes\">\n<noframes>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<h2>Frame Alert</h2>\n<p>This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client. Link to <a href=\"it/necst/grabnrun/package-summary.html\">Non-frame version</a>.</p>\n</noframes>\n</frameset>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/SecureDexClassLoader.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>SecureDexClassLoader</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"SecureDexClassLoader\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li class=\"navBarCell1Rev\">Class</li>\n<li><a href=\"class-use/SecureDexClassLoader.html\">Use</a></li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev Class</li>\n<li><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">Next Class</span></a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/SecureDexClassLoader.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureDexClassLoader.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<div>\n<ul class=\"subNavList\">\n<li>Summary:&nbsp;</li>\n<li>Nested&nbsp;|&nbsp;</li>\n<li>Field&nbsp;|&nbsp;</li>\n<li>Constr&nbsp;|&nbsp;</li>\n<li><a href=\"#method_summary\">Method</a></li>\n</ul>\n<ul class=\"subNavList\">\n<li>Detail:&nbsp;</li>\n<li>Field&nbsp;|&nbsp;</li>\n<li>Constr&nbsp;|&nbsp;</li>\n<li><a href=\"#method_detail\">Method</a></li>\n</ul>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<!-- ======== START OF CLASS DATA ======== -->\n<div class=\"header\">\n<div class=\"subTitle\">it.necst.grabnrun</div>\n<h2 title=\"Class SecureDexClassLoader\" class=\"title\">Class SecureDexClassLoader</h2>\n</div>\n<div class=\"contentContainer\">\n<ul class=\"inheritance\">\n<li><a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\">java.lang.Object</a></li>\n<li>\n<ul class=\"inheritance\">\n<li>it.necst.grabnrun.SecureDexClassLoader</li>\n</ul>\n</li>\n</ul>\n<div class=\"description\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<hr>\n<br>\n<pre>public class <span class=\"strong\">SecureDexClassLoader</span>\nextends <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\">Object</a></pre>\n<div class=\"block\">A class that provides an extension of default <a href=\"http://d.android.com/reference/dalvik/system/DexClassLoader.html?is-external=true\" title=\"class or interface in dalvik.system\"><code>DexClassLoader</code></a> \n provided by the Android system and it is used to load classes \n from jar and apk container files including a classes.dex entry in a secure way.\n <p>\n In order to instantiate this class a call to \n <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\"><code>SecureLoaderFactory.createDexClassLoader(String, String, ClassLoader, Map)</code></a> \n must be performed.\n <p>\n <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> ensures integrity of loaded external remote \n classes by comparing them with the developer certificate, which\n is retrieved either by a provided associative map between package names \n and certificate remote URL or by simply reverting the \n first two words of the package name of the loaded class and then \n by adding each following word in the same order and separated by \n a slash \"/\".\n <p>\n Package name reversion example:<br>\n Class name = it.necst.grabnrun.example.TestClassImpl<br>\n Constructed URL = https://necst.it/grabnrun/example<br>\n Final certificate location = https://necst.it/grabnrun/example/certificate.pem<br>\n <p>\n A request is pointed to the final certificate location and if \n the file is found, it is imported in the local private \n application directory.\n <p>\n Please note that in the current implementation certificates obtained \n by reverting package name must have been saved at the described \n location as \"certificate.pem\". Moreover all the certificates must \n fit requirements of a standard <a href=\"http://d.android.com/reference/java/security/cert/X509Certificate.html?is-external=true\" title=\"class or interface in java.security.cert\"><code>X509Certificate</code></a>, \n they must be valid in the current time frame and of course they must have been \n used to sign the jar or apk, which contains the classes to be loaded.\n <p>\n If any of these previous requirements is violated no class is loaded \n and this class returns without executing any class loading operation.</div>\n<dl><dt><span class=\"strong\">Author:</span></dt>\n  <dd>Luca Falsina</dd></dl>\n</li>\n</ul>\n</div>\n<div class=\"summary\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<!-- ========== METHOD SUMMARY =========== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"method_summary\">\n<!--   -->\n</a>\n<h3>Method Summary</h3>\n<table class=\"overviewSummary\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Method Summary table, listing methods, and an explanation\">\n<caption><span>Methods</span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colFirst\" scope=\"col\">Modifier and Type</th>\n<th class=\"colLast\" scope=\"col\">Method and Description</th>\n</tr>\n<tr class=\"altColor\">\n<td class=\"colFirst\"><code><a href=\"http://d.android.com/reference/java/lang/Class.html?is-external=true\" title=\"class or interface in java.lang\">Class</a>&lt;?&gt;</code></td>\n<td class=\"colLast\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html#loadClass(java.lang.String)\">loadClass</a></strong>(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;className)</code>&nbsp;</td>\n</tr>\n<tr class=\"rowColor\">\n<td class=\"colFirst\"><code>void</code></td>\n<td class=\"colLast\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html#wipeOutPrivateAppCachedData(boolean,%20boolean)\">wipeOutPrivateAppCachedData</a></strong>(boolean&nbsp;containerPrivateFolder,\n                           boolean&nbsp;certificatePrivateFolder)</code>\n<div class=\"block\">Sometimes it may be useful to remove those data that have been cached in \n the private application folders (basically for performance reason or for making \n <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> works also partially offline).</div>\n</td>\n</tr>\n</table>\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"methods_inherited_from_class_java.lang.Object\">\n<!--   -->\n</a>\n<h3>Methods inherited from class&nbsp;java.lang.<a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\">Object</a></h3>\n<code><a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#equals(java.lang.Object)\" title=\"class or interface in java.lang\">equals</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#getClass()\" title=\"class or interface in java.lang\">getClass</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#hashCode()\" title=\"class or interface in java.lang\">hashCode</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#notify()\" title=\"class or interface in java.lang\">notify</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#notifyAll()\" title=\"class or interface in java.lang\">notifyAll</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#toString()\" title=\"class or interface in java.lang\">toString</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#wait()\" title=\"class or interface in java.lang\">wait</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#wait(long)\" title=\"class or interface in java.lang\">wait</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#wait(long,%20int)\" title=\"class or interface in java.lang\">wait</a></code></li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</div>\n<div class=\"details\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<!-- ============ METHOD DETAIL ========== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"method_detail\">\n<!--   -->\n</a>\n<h3>Method Detail</h3>\n<a name=\"loadClass(java.lang.String)\">\n<!--   -->\n</a>\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<h4>loadClass</h4>\n<pre>public&nbsp;<a href=\"http://d.android.com/reference/java/lang/Class.html?is-external=true\" title=\"class or interface in java.lang\">Class</a>&lt;?&gt;&nbsp;loadClass(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;className)\n                   throws <a href=\"http://d.android.com/reference/java/lang/ClassNotFoundException.html?is-external=true\" title=\"class or interface in java.lang\">ClassNotFoundException</a></pre>\n<dl><dt><span class=\"strong\">Parameters:</span></dt><dd><code>className</code> - the full class name to load. It must fit the form \"package name + . + class name\".\n  A valid full class name is for example \"it.polimi.myapplication.classA\"; while \"classA\" is not\n  enough since it misses the package name and so <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> will not find any\n  class to load.</dd>\n<dt><span class=\"strong\">Returns:</span></dt><dd>Either a class that needs to be casted at runtime accordingly to className if the verification\n  process succeeds or a <code>null</code> pointer in case that at least one of the security\n  constraints for secure dynamic class loading is violated.</dd>\n<dt><span class=\"strong\">Throws:</span></dt>\n<dd><code><a href=\"http://d.android.com/reference/java/lang/ClassNotFoundException.html?is-external=true\" title=\"class or interface in java.lang\">ClassNotFoundException</a></code> - this exception is raised whenever no security constraint is violated but still the target class is\n  not found in any of the available containers used to instantiate this <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> object.</dd><dt><span class=\"strong\">See Also:</span></dt><dd><a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true#loadClass(java.lang.String)\" title=\"class or interface in java.lang\"><code>ClassLoader.loadClass(java.lang.String)</code></a></dd></dl>\n</li>\n</ul>\n<a name=\"wipeOutPrivateAppCachedData(boolean, boolean)\">\n<!--   -->\n</a>\n<ul class=\"blockListLast\">\n<li class=\"blockList\">\n<h4>wipeOutPrivateAppCachedData</h4>\n<pre>public&nbsp;void&nbsp;wipeOutPrivateAppCachedData(boolean&nbsp;containerPrivateFolder,\n                               boolean&nbsp;certificatePrivateFolder)</pre>\n<div class=\"block\">Sometimes it may be useful to remove those data that have been cached in \n the private application folders (basically for performance reason or for making \n <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> works also partially offline). A call to this method solves the issue.\n <p>\n Please notice that a call to this method with both the parameters set to false \n has no effect.\n <p>\n In any of the other cases the content of the related folder(s) will be erased and \n since some of the data may have been used by <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> instances, it is \n required to the caller to create a new <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> object through \n <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><code>SecureLoaderFactory</code></a> since the already present object is going to be disabled \n from loading classes dynamically.</div>\n<dl><dt><span class=\"strong\">Parameters:</span></dt><dd><code>containerPrivateFolder</code> - if the private folder where jar and apk containers downloaded from remote URL or imported from local storage needs to be wiped out.</dd><dd><code>certificatePrivateFolder</code> - if the private folder containing certificates needs to be wiped out.</dd></dl>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</div>\n</div>\n<!-- ========= END OF CLASS DATA ========= -->\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li class=\"navBarCell1Rev\">Class</li>\n<li><a href=\"class-use/SecureDexClassLoader.html\">Use</a></li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev Class</li>\n<li><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">Next Class</span></a></li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/SecureDexClassLoader.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureDexClassLoader.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<div>\n<ul class=\"subNavList\">\n<li>Summary:&nbsp;</li>\n<li>Nested&nbsp;|&nbsp;</li>\n<li>Field&nbsp;|&nbsp;</li>\n<li>Constr&nbsp;|&nbsp;</li>\n<li><a href=\"#method_summary\">Method</a></li>\n</ul>\n<ul class=\"subNavList\">\n<li>Detail:&nbsp;</li>\n<li>Field&nbsp;|&nbsp;</li>\n<li>Constr&nbsp;|&nbsp;</li>\n<li><a href=\"#method_detail\">Method</a></li>\n</ul>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/SecureLoaderFactory.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>SecureLoaderFactory</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"SecureLoaderFactory\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li class=\"navBarCell1Rev\">Class</li>\n<li><a href=\"class-use/SecureLoaderFactory.html\">Use</a></li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">Prev Class</span></a></li>\n<li>Next Class</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/SecureLoaderFactory.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureLoaderFactory.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<div>\n<ul class=\"subNavList\">\n<li>Summary:&nbsp;</li>\n<li>Nested&nbsp;|&nbsp;</li>\n<li><a href=\"#field_summary\">Field</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#constructor_summary\">Constr</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#method_summary\">Method</a></li>\n</ul>\n<ul class=\"subNavList\">\n<li>Detail:&nbsp;</li>\n<li><a href=\"#field_detail\">Field</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#constructor_detail\">Constr</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#method_detail\">Method</a></li>\n</ul>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<!-- ======== START OF CLASS DATA ======== -->\n<div class=\"header\">\n<div class=\"subTitle\">it.necst.grabnrun</div>\n<h2 title=\"Class SecureLoaderFactory\" class=\"title\">Class SecureLoaderFactory</h2>\n</div>\n<div class=\"contentContainer\">\n<ul class=\"inheritance\">\n<li><a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\">java.lang.Object</a></li>\n<li>\n<ul class=\"inheritance\">\n<li>it.necst.grabnrun.SecureLoaderFactory</li>\n</ul>\n</li>\n</ul>\n<div class=\"description\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<hr>\n<br>\n<pre>public class <span class=\"strong\">SecureLoaderFactory</span>\nextends <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\">Object</a></pre>\n<div class=\"block\">A Factory class that generates instances of classes used to \n retrieve dynamic code in a secure way at run time.</div>\n<dl><dt><span class=\"strong\">Author:</span></dt>\n  <dd>Luca Falsina</dd></dl>\n</li>\n</ul>\n</div>\n<div class=\"summary\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<!-- =========== FIELD SUMMARY =========== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"field_summary\">\n<!--   -->\n</a>\n<h3>Field Summary</h3>\n<table class=\"overviewSummary\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Field Summary table, listing fields, and an explanation\">\n<caption><span>Fields</span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colFirst\" scope=\"col\">Modifier and Type</th>\n<th class=\"colLast\" scope=\"col\">Field and Description</th>\n</tr>\n<tr class=\"altColor\">\n<td class=\"colFirst\"><code>static int</code></td>\n<td class=\"colLast\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\">DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY</a></strong></code>\n<div class=\"block\">When a remote container URL is encountered, this field specifies the default time interval, \n expressed in days, before which a local copy of a remote container, stored in an \n application-private directory, will be considered fresh and so acceptable to be cached.</div>\n</td>\n</tr>\n</table>\n</li>\n</ul>\n<!-- ======== CONSTRUCTOR SUMMARY ======== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"constructor_summary\">\n<!--   -->\n</a>\n<h3>Constructor Summary</h3>\n<table class=\"overviewSummary\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Constructor Summary table, listing constructors, and an explanation\">\n<caption><span>Constructors</span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colOne\" scope=\"col\">Constructor and Description</th>\n</tr>\n<tr class=\"altColor\">\n<td class=\"colOne\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#SecureLoaderFactory(ContextWrapper)\">SecureLoaderFactory</a></strong>(ContextWrapper&nbsp;parentContextWrapper)</code>\n<div class=\"block\">Creates a <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><code>SecureLoaderFactory</code></a> used to check and generate instances \n from secure dynamic code loader classes.</div>\n</td>\n</tr>\n<tr class=\"rowColor\">\n<td class=\"colOne\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#SecureLoaderFactory(ContextWrapper,%20int)\">SecureLoaderFactory</a></strong>(ContextWrapper&nbsp;parentContextWrapper,\n                   int&nbsp;daysBeforeContainerCacheExpiration)</code>\n<div class=\"block\">This constructor works exactly as the previous one but it also allows to \n decide the time interval in days before which a local copy of a remote container, stored in an \n application-private directory, will be considered fresh and so acceptable to be cached.</div>\n</td>\n</tr>\n</table>\n</li>\n</ul>\n<!-- ========== METHOD SUMMARY =========== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"method_summary\">\n<!--   -->\n</a>\n<h3>Method Summary</h3>\n<table class=\"overviewSummary\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Method Summary table, listing methods, and an explanation\">\n<caption><span>Methods</span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colFirst\" scope=\"col\">Modifier and Type</th>\n<th class=\"colLast\" scope=\"col\">Method and Description</th>\n</tr>\n<tr class=\"altColor\">\n<td class=\"colFirst\"><code><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></code></td>\n<td class=\"colLast\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\">createDexClassLoader</a></strong>(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;dexPath,\n                    <a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;libraryPath,\n                    <a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true\" title=\"class or interface in java.lang\">ClassLoader</a>&nbsp;parent,\n                    <a href=\"http://d.android.com/reference/java/util/Map.html?is-external=true\" title=\"class or interface in java.util\">Map</a>&lt;<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>,<a href=\"http://d.android.com/reference/java/net/URL.html?is-external=true\" title=\"class or interface in java.net\">URL</a>&gt;&nbsp;packageNameToCertificateMap)</code>\n<div class=\"block\">Creates a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> that finds interpreted and native code in a set of\n provided locations (either local or remote via HTTP or HTTPS) in dexPath.</div>\n</td>\n</tr>\n<tr class=\"rowColor\">\n<td class=\"colFirst\"><code><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></code></td>\n<td class=\"colLast\"><code><strong><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map,%20boolean)\">createDexClassLoader</a></strong>(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;dexPath,\n                    <a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;libraryPath,\n                    <a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true\" title=\"class or interface in java.lang\">ClassLoader</a>&nbsp;parent,\n                    <a href=\"http://d.android.com/reference/java/util/Map.html?is-external=true\" title=\"class or interface in java.util\">Map</a>&lt;<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>,<a href=\"http://d.android.com/reference/java/net/URL.html?is-external=true\" title=\"class or interface in java.net\">URL</a>&gt;&nbsp;packageNameToCertificateMap,\n                    boolean&nbsp;performLazyEvaluation)</code>\n<div class=\"block\">This method returns a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> instance in the same way as it is \n explained in the <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\"><code>createDexClassLoader(String, String, ClassLoader, Map)</code></a>.</div>\n</td>\n</tr>\n</table>\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"methods_inherited_from_class_java.lang.Object\">\n<!--   -->\n</a>\n<h3>Methods inherited from class&nbsp;java.lang.<a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\">Object</a></h3>\n<code><a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#equals(java.lang.Object)\" title=\"class or interface in java.lang\">equals</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#getClass()\" title=\"class or interface in java.lang\">getClass</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#hashCode()\" title=\"class or interface in java.lang\">hashCode</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#notify()\" title=\"class or interface in java.lang\">notify</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#notifyAll()\" title=\"class or interface in java.lang\">notifyAll</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#toString()\" title=\"class or interface in java.lang\">toString</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#wait()\" title=\"class or interface in java.lang\">wait</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#wait(long)\" title=\"class or interface in java.lang\">wait</a>, <a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true#wait(long,%20int)\" title=\"class or interface in java.lang\">wait</a></code></li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</div>\n<div class=\"details\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<!-- ============ FIELD DETAIL =========== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"field_detail\">\n<!--   -->\n</a>\n<h3>Field Detail</h3>\n<a name=\"DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\">\n<!--   -->\n</a>\n<ul class=\"blockListLast\">\n<li class=\"blockList\">\n<h4>DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY</h4>\n<pre>public static final&nbsp;int DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY</pre>\n<div class=\"block\">When a remote container URL is encountered, this field specifies the default time interval, \n expressed in days, before which a local copy of a remote container, stored in an \n application-private directory, will be considered fresh and so acceptable to be cached.\n <p>\n On the other hand, all of those containers, whose life time is greater than this\n field value, will be immediately erased from the device storage in stead of being cached.\n <p>\n You can change this duration by generating the <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><code>SecureLoaderFactory</code></a> instance\n with <code>SecureLoaderFactory#SecureLoaderFactory(android.content.ContextWrapper, int)</code>.</div>\n<dl><dt><span class=\"strong\">See Also:</span></dt><dd><a href=\"../../../constant-values.html#it.necst.grabnrun.SecureLoaderFactory.DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\">Constant Field Values</a></dd></dl>\n</li>\n</ul>\n</li>\n</ul>\n<!-- ========= CONSTRUCTOR DETAIL ======== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"constructor_detail\">\n<!--   -->\n</a>\n<h3>Constructor Detail</h3>\n<a name=\"SecureLoaderFactory(ContextWrapper)\">\n<!--   -->\n</a>\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<h4>SecureLoaderFactory</h4>\n<pre>public&nbsp;SecureLoaderFactory(ContextWrapper&nbsp;parentContextWrapper)</pre>\n<div class=\"block\">Creates a <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><code>SecureLoaderFactory</code></a> used to check and generate instances \n from secure dynamic code loader classes.\n <p>\n It requires a <a href=\"http://d.android.com/reference/android/content/ContextWrapper.html?is-external=true\" title=\"class or interface in android.content\"><code>ContextWrapper</code></a> (i.e. the launching activity) which \n should be used to manage and retrieve internal directories \n of the application.</div>\n<dl><dt><span class=\"strong\">Parameters:</span></dt><dd><code>parentContextWrapper</code> - The content wrapper coming from the launching Activity.</dd></dl>\n</li>\n</ul>\n<a name=\"SecureLoaderFactory(ContextWrapper, int)\">\n<!--   -->\n</a>\n<ul class=\"blockListLast\">\n<li class=\"blockList\">\n<h4>SecureLoaderFactory</h4>\n<pre>public&nbsp;SecureLoaderFactory(ContextWrapper&nbsp;parentContextWrapper,\n                   int&nbsp;daysBeforeContainerCacheExpiration)</pre>\n<div class=\"block\">This constructor works exactly as the previous one but it also allows to \n decide the time interval in days before which a local copy of a remote container, stored in an \n application-private directory, will be considered fresh and so acceptable to be cached.\n <p>\n If a negative value is provided for the second parameter, <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><code>SecureLoaderFactory</code></a> will be \n instantiated with the time interval in days equal to <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY\"><code>DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY</code></a>.</div>\n<dl><dt><span class=\"strong\">Parameters:</span></dt><dd><code>parentContextWrapper</code> - The content wrapper coming from the launching Activity.</dd><dd><code>daysBeforeContainerCacheExpiration</code> - The non negative value of days for which a local copy of a remote container imported into\n  an application private folder is considered fresh and so acceptable to be cached.</dd></dl>\n</li>\n</ul>\n</li>\n</ul>\n<!-- ============ METHOD DETAIL ========== -->\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"method_detail\">\n<!--   -->\n</a>\n<h3>Method Detail</h3>\n<a name=\"createDexClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader, java.util.Map)\">\n<!--   -->\n</a>\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<h4>createDexClassLoader</h4>\n<pre>public&nbsp;<a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a>&nbsp;createDexClassLoader(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;dexPath,\n                                        <a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;libraryPath,\n                                        <a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true\" title=\"class or interface in java.lang\">ClassLoader</a>&nbsp;parent,\n                                        <a href=\"http://d.android.com/reference/java/util/Map.html?is-external=true\" title=\"class or interface in java.util\">Map</a>&lt;<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>,<a href=\"http://d.android.com/reference/java/net/URL.html?is-external=true\" title=\"class or interface in java.net\">URL</a>&gt;&nbsp;packageNameToCertificateMap)</pre>\n<div class=\"block\">Creates a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> that finds interpreted and native code in a set of\n provided locations (either local or remote via HTTP or HTTPS) in dexPath.\n Interpreted classes are found in a set of DEX files contained in Jar or Apk files and \n stored into an application-private, writable directory.\n <p>\n Before executing one of these classes the signature of the target class is \n verified against the certificate associated with its package name.\n Certificates location are provided by filling appropriately packageNameToCertificateMap;\n each package name must be linked with the remote location of the certificate that\n should be used to validate all the classes of that package. It's important \n that each one of these locations uses HTTPS as its protocol; otherwise this \n choice will be enforced!\n If a class package name do not match any of the provided entries in the map, \n certificate location will be constructed by simply reverting package name and \n transforming it into a web-based URL using HTTPS.\n <p>\n Note that this method returns <code>null</code> if no matching Jar or Apk file is found at the\n provided dexPath parameter; otherwise a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> instance is returned.\n <p>\n Dynamic class loading with the returned <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> will fail whether\n at least one of these conditions is not accomplished: target class is not found in dexPath\n or is in a missing remote container (i.e. Internet connectivity is not present), missing or\n invalid (i.e. expired) certificate is associated with the package name of the target class,\n target class signature check fails against the associated certificate.</div>\n<dl><dt><span class=\"strong\">Parameters:</span></dt><dd><code>dexPath</code> - the list of jar/apk files containing classes and resources; these paths could\n  be either local URLs pointing to a location in the device or URLs that links\n  to a resource stored in the web via HTTP/HTTPS. In the latter case, if Internet\n  connectivity is available, the resource will be imported in a private-application \n  directory before being used.</dd><dd><code>libraryPath</code> - the list of directories containing native libraries; it may be <code>null</code>.</dd><dd><code>parent</code> - the parent class loader.</dd><dd><code>packageNameToCertificateMap</code> - a map that couples each package name to a URL which contains the certificate\n  that must be used to validate all the classes that belong to that package\n  before launching them at run time. Please notice that any URL in this map \n  using HTTP protocol will be enforced to use HTTPS in stead.</dd>\n<dt><span class=\"strong\">Returns:</span></dt><dd>a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> object which can be used to load dynamic code securely and \n  uses a Eager strategy for container signature verification.</dd></dl>\n</li>\n</ul>\n<a name=\"createDexClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader, java.util.Map, boolean)\">\n<!--   -->\n</a>\n<ul class=\"blockListLast\">\n<li class=\"blockList\">\n<h4>createDexClassLoader</h4>\n<pre>public&nbsp;<a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a>&nbsp;createDexClassLoader(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;dexPath,\n                                        <a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;libraryPath,\n                                        <a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true\" title=\"class or interface in java.lang\">ClassLoader</a>&nbsp;parent,\n                                        <a href=\"http://d.android.com/reference/java/util/Map.html?is-external=true\" title=\"class or interface in java.util\">Map</a>&lt;<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>,<a href=\"http://d.android.com/reference/java/net/URL.html?is-external=true\" title=\"class or interface in java.net\">URL</a>&gt;&nbsp;packageNameToCertificateMap,\n                                        boolean&nbsp;performLazyEvaluation)</pre>\n<div class=\"block\">This method returns a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> instance in the same way as it is \n explained in the <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\"><code>createDexClassLoader(String, String, ClassLoader, Map)</code></a>. In addition it is \n possible to specify the mode in which the signature verification process will be carried.\n <p>\n In particular by setting the performLazyEvaluation parameter on true, \n a lazy verification process will be chosen. This means that the signature\n of the single container associated with the target class will be evaluated \n only when <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html#loadClass(java.lang.String)\"><code>SecureDexClassLoader.loadClass(String)</code></a> will be invoked . \n If this check succeeds the target class will be loaded.\n <p>\n On the other hand, by setting the parameter performLazyEvaluation to false \n an eager evaluation will be carried out. This means that before returning this \n object, the signature verification procedure will be carried out on all the \n provided containers in a CONCURRENT way and all of those that do not succeed in the process will\n be blocked from loading their classes in the following loadClass() method calls.\n <p>\n The use of one mode in stead of the other is merely a performance-related choice.\n If you do not care that much about it, just invoke\n <a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\"><code>createDexClassLoader(String, String, ClassLoader, Map)</code></a> \n which does not require to provide the performLazyEvaluation\n parameter and it will perform an Eager evaluation. Otherwise as a general guideline, \n prefer the lazy mode whenever you think that you won't need to load classes \n from all the provided containers.</div>\n<dl><dt><span class=\"strong\">Parameters:</span></dt><dd><code>dexPath</code> - the list of jar/apk files containing classes and resources; these paths could\n  be either local URLs pointing to a location in the device or URLs that links\n  to a resource stored in the web via HTTP/HTTPS. In the latter case, if Internet\n  connectivity is available, the resource will be imported in a private-application \n  directory before being used.</dd><dd><code>libraryPath</code> - the list of directories containing native libraries; it may be <code>null</code>.</dd><dd><code>parent</code> - the parent class loader.</dd><dd><code>packageNameToCertificateMap</code> - a map that couples each package name to a URL which contains the certificate\n  that must be used to validate all the classes that belong to that package\n  before launching them at run time. Please notice that any URL in this map \n  using HTTP protocol will be enforced to use HTTPS in stead.</dd><dd><code>performLazyEvaluation</code> - the mode in which the verification will be handled. True for lazy verification;\n  false for the eager one.</dd>\n<dt><span class=\"strong\">Returns:</span></dt><dd>a <a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> object which can be used to load dynamic code securely and \n  uses a either Lazy or an Eager strategy for container signature verification depending\n  on the last parameter provided to this constructor.</dd></dl>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</div>\n</div>\n<!-- ========= END OF CLASS DATA ========= -->\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li class=\"navBarCell1Rev\">Class</li>\n<li><a href=\"class-use/SecureLoaderFactory.html\">Use</a></li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">Prev Class</span></a></li>\n<li>Next Class</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/SecureLoaderFactory.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureLoaderFactory.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<div>\n<ul class=\"subNavList\">\n<li>Summary:&nbsp;</li>\n<li>Nested&nbsp;|&nbsp;</li>\n<li><a href=\"#field_summary\">Field</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#constructor_summary\">Constr</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#method_summary\">Method</a></li>\n</ul>\n<ul class=\"subNavList\">\n<li>Detail:&nbsp;</li>\n<li><a href=\"#field_detail\">Field</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#constructor_detail\">Constr</a>&nbsp;|&nbsp;</li>\n<li><a href=\"#method_detail\">Method</a></li>\n</ul>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/class-use/SecureDexClassLoader.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Uses of Class it.necst.grabnrun.SecureDexClassLoader</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"Uses of Class it.necst.grabnrun.SecureDexClassLoader\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li><a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">Class</a></li>\n<li class=\"navBarCell1Rev\">Use</li>\n<li><a href=\"../../../../overview-tree.html\">Tree</a></li>\n<li><a href=\"../../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../../index.html?it/necst/grabnrun/class-use/SecureDexClassLoader.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureDexClassLoader.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h2 title=\"Uses of Class it.necst.grabnrun.SecureDexClassLoader\" class=\"title\">Uses of Class<br>it.necst.grabnrun.SecureDexClassLoader</h2>\n</div>\n<div class=\"classUseContainer\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"it.necst.grabnrun\">\n<!--   -->\n</a>\n<h3>Uses of <a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a> in <a href=\"../../../../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a></h3>\n<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Use table, listing methods, and an explanation\">\n<caption><span>Methods in <a href=\"../../../../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a> that return <a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colFirst\" scope=\"col\">Modifier and Type</th>\n<th class=\"colLast\" scope=\"col\">Method and Description</th>\n</tr>\n<tbody>\n<tr class=\"altColor\">\n<td class=\"colFirst\"><code><a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></code></td>\n<td class=\"colLast\"><span class=\"strong\">SecureLoaderFactory.</span><code><strong><a href=\"../../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\">createDexClassLoader</a></strong>(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;dexPath,\n                    <a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;libraryPath,\n                    <a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true\" title=\"class or interface in java.lang\">ClassLoader</a>&nbsp;parent,\n                    <a href=\"http://d.android.com/reference/java/util/Map.html?is-external=true\" title=\"class or interface in java.util\">Map</a>&lt;<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>,<a href=\"http://d.android.com/reference/java/net/URL.html?is-external=true\" title=\"class or interface in java.net\">URL</a>&gt;&nbsp;packageNameToCertificateMap)</code>\n<div class=\"block\">Creates a <a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> that finds interpreted and native code in a set of\n provided locations (either local or remote via HTTP or HTTPS) in dexPath.</div>\n</td>\n</tr>\n<tr class=\"rowColor\">\n<td class=\"colFirst\"><code><a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></code></td>\n<td class=\"colLast\"><span class=\"strong\">SecureLoaderFactory.</span><code><strong><a href=\"../../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map,%20boolean)\">createDexClassLoader</a></strong>(<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;dexPath,\n                    <a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>&nbsp;libraryPath,\n                    <a href=\"http://d.android.com/reference/java/lang/ClassLoader.html?is-external=true\" title=\"class or interface in java.lang\">ClassLoader</a>&nbsp;parent,\n                    <a href=\"http://d.android.com/reference/java/util/Map.html?is-external=true\" title=\"class or interface in java.util\">Map</a>&lt;<a href=\"http://d.android.com/reference/java/lang/String.html?is-external=true\" title=\"class or interface in java.lang\">String</a>,<a href=\"http://d.android.com/reference/java/net/URL.html?is-external=true\" title=\"class or interface in java.net\">URL</a>&gt;&nbsp;packageNameToCertificateMap,\n                    boolean&nbsp;performLazyEvaluation)</code>\n<div class=\"block\">This method returns a <a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><code>SecureDexClassLoader</code></a> instance in the same way as it is \n explained in the <a href=\"../../../../it/necst/grabnrun/SecureLoaderFactory.html#createDexClassLoader(java.lang.String,%20java.lang.String,%20java.lang.ClassLoader,%20java.util.Map)\"><code>SecureLoaderFactory.createDexClassLoader(String, String, ClassLoader, Map)</code></a>.</div>\n</td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n</li>\n</ul>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li><a href=\"../../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">Class</a></li>\n<li class=\"navBarCell1Rev\">Use</li>\n<li><a href=\"../../../../overview-tree.html\">Tree</a></li>\n<li><a href=\"../../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../../index.html?it/necst/grabnrun/class-use/SecureDexClassLoader.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureDexClassLoader.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/class-use/SecureLoaderFactory.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Uses of Class it.necst.grabnrun.SecureLoaderFactory</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"Uses of Class it.necst.grabnrun.SecureLoaderFactory\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li><a href=\"../../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">Class</a></li>\n<li class=\"navBarCell1Rev\">Use</li>\n<li><a href=\"../../../../overview-tree.html\">Tree</a></li>\n<li><a href=\"../../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../../index.html?it/necst/grabnrun/class-use/SecureLoaderFactory.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureLoaderFactory.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h2 title=\"Uses of Class it.necst.grabnrun.SecureLoaderFactory\" class=\"title\">Uses of Class<br>it.necst.grabnrun.SecureLoaderFactory</h2>\n</div>\n<div class=\"classUseContainer\">No usage of it.necst.grabnrun.SecureLoaderFactory</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li><a href=\"../../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">Class</a></li>\n<li class=\"navBarCell1Rev\">Use</li>\n<li><a href=\"../../../../overview-tree.html\">Tree</a></li>\n<li><a href=\"../../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../../index.html?it/necst/grabnrun/class-use/SecureLoaderFactory.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"SecureLoaderFactory.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/package-frame.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>it.necst.grabnrun</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<h1 class=\"bar\"><a href=\"../../../it/necst/grabnrun/package-summary.html\" target=\"classFrame\">it.necst.grabnrun</a></h1>\n<div class=\"indexContainer\">\n<h2 title=\"Classes\">Classes</h2>\n<ul title=\"Classes\">\n<li><a href=\"SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\" target=\"classFrame\">SecureDexClassLoader</a></li>\n<li><a href=\"SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\" target=\"classFrame\">SecureLoaderFactory</a></li>\n</ul>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/package-summary.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>it.necst.grabnrun</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"it.necst.grabnrun\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li><a href=\"package-use.html\">Use</a></li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev Package</li>\n<li>Next Package</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/package-summary.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"package-summary.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 title=\"Package\" class=\"title\">Package&nbsp;it.necst.grabnrun</h1>\n</div>\n<div class=\"contentContainer\">\n<ul class=\"blockList\">\n<li class=\"blockList\">\n<table class=\"packageSummary\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Class Summary table, listing classes, and an explanation\">\n<caption><span>Class Summary</span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colFirst\" scope=\"col\">Class</th>\n<th class=\"colLast\" scope=\"col\">Description</th>\n</tr>\n<tbody>\n<tr class=\"altColor\">\n<td class=\"colFirst\"><a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\">SecureDexClassLoader</a></td>\n<td class=\"colLast\">\n<div class=\"block\">A class that provides an extension of default <a href=\"http://d.android.com/reference/dalvik/system/DexClassLoader.html?is-external=true\" title=\"class or interface in dalvik.system\"><code>DexClassLoader</code></a> \n provided by the Android system and it is used to load classes \n from jar and apk container files including a classes.dex entry in a secure way.</div>\n</td>\n</tr>\n<tr class=\"rowColor\">\n<td class=\"colFirst\"><a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\">SecureLoaderFactory</a></td>\n<td class=\"colLast\">\n<div class=\"block\">A Factory class that generates instances of classes used to \n retrieve dynamic code in a secure way at run time.</div>\n</td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li><a href=\"package-use.html\">Use</a></li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev Package</li>\n<li>Next Package</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/package-summary.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"package-summary.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/package-tree.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>it.necst.grabnrun Class Hierarchy</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"it.necst.grabnrun Class Hierarchy\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li class=\"navBarCell1Rev\">Tree</li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/package-tree.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"package-tree.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 class=\"title\">Hierarchy For Package it.necst.grabnrun</h1>\n</div>\n<div class=\"contentContainer\">\n<h2 title=\"Class Hierarchy\">Class Hierarchy</h2>\n<ul>\n<li type=\"circle\">java.lang.<a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\"><span class=\"strong\">Object</span></a>\n<ul>\n<li type=\"circle\">it.necst.grabnrun.<a href=\"../../../it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">SecureDexClassLoader</span></a></li>\n<li type=\"circle\">it.necst.grabnrun.<a href=\"../../../it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">SecureLoaderFactory</span></a></li>\n</ul>\n</li>\n</ul>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li class=\"navBarCell1Rev\">Tree</li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/package-tree.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"package-tree.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/it/necst/grabnrun/package-use.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Uses of Package it.necst.grabnrun</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"../../../stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"Uses of Package it.necst.grabnrun\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li class=\"navBarCell1Rev\">Use</li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/package-use.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"package-use.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 title=\"Uses of Package it.necst.grabnrun\" class=\"title\">Uses of Package<br>it.necst.grabnrun</h1>\n</div>\n<div class=\"contentContainer\">\n<ul class=\"blockList\">\n<li class=\"blockList\"><a name=\"it.necst.grabnrun\">\n<!--   -->\n</a>\n<table border=\"0\" cellpadding=\"3\" cellspacing=\"0\" summary=\"Use table, listing classes, and an explanation\">\n<caption><span>Classes in <a href=\"../../../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a> used by <a href=\"../../../it/necst/grabnrun/package-summary.html\">it.necst.grabnrun</a></span><span class=\"tabEnd\">&nbsp;</span></caption>\n<tr>\n<th class=\"colOne\" scope=\"col\">Class and Description</th>\n</tr>\n<tbody>\n<tr class=\"altColor\">\n<td class=\"colOne\"><a href=\"../../../it/necst/grabnrun/class-use/SecureDexClassLoader.html#it.necst.grabnrun\">SecureDexClassLoader</a>\n<div class=\"block\">A class that provides an extension of default <a href=\"http://d.android.com/reference/dalvik/system/DexClassLoader.html?is-external=true\" title=\"class or interface in dalvik.system\"><code>DexClassLoader</code></a> \n provided by the Android system and it is used to load classes \n from jar and apk container files including a classes.dex entry in a secure way.</div>\n</td>\n</tr>\n</tbody>\n</table>\n</li>\n</ul>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"../../../it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li class=\"navBarCell1Rev\">Use</li>\n<li><a href=\"package-tree.html\">Tree</a></li>\n<li><a href=\"../../../deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"../../../index-files/index-1.html\">Index</a></li>\n<li><a href=\"../../../help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"../../../index.html?it/necst/grabnrun/package-use.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"package-use.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"../../../allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/overview-tree.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<!-- NewPage -->\n<html lang=\"en\">\n<head>\n<!-- Generated by javadoc (version 1.7.0_72) on Mon Nov 17 19:29:57 CET 2014 -->\n<title>Class Hierarchy</title>\n<meta name=\"date\" content=\"2014-11-17\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" title=\"Style\">\n</head>\n<body>\n<script type=\"text/javascript\"><!--\n    if (location.href.indexOf('is-external=true') == -1) {\n        parent.document.title=\"Class Hierarchy\";\n    }\n//-->\n</script>\n<noscript>\n<div>JavaScript is disabled on your browser.</div>\n</noscript>\n<!-- ========= START OF TOP NAVBAR ======= -->\n<div class=\"topNav\"><a name=\"navbar_top\">\n<!--   -->\n</a><a href=\"#skip-navbar_top\" title=\"Skip navigation links\"></a><a name=\"navbar_top_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li class=\"navBarCell1Rev\">Tree</li>\n<li><a href=\"deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li><a href=\"help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?overview-tree.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"overview-tree.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_top\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_top\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_top\">\n<!--   -->\n</a></div>\n<!-- ========= END OF TOP NAVBAR ========= -->\n<div class=\"header\">\n<h1 class=\"title\">Hierarchy For All Packages</h1>\n<span class=\"strong\">Package Hierarchies:</span>\n<ul class=\"horizontal\">\n<li><a href=\"it/necst/grabnrun/package-tree.html\">it.necst.grabnrun</a></li>\n</ul>\n</div>\n<div class=\"contentContainer\">\n<h2 title=\"Class Hierarchy\">Class Hierarchy</h2>\n<ul>\n<li type=\"circle\">java.lang.<a href=\"http://d.android.com/reference/java/lang/Object.html?is-external=true\" title=\"class or interface in java.lang\"><span class=\"strong\">Object</span></a>\n<ul>\n<li type=\"circle\">it.necst.grabnrun.<a href=\"it/necst/grabnrun/SecureDexClassLoader.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">SecureDexClassLoader</span></a></li>\n<li type=\"circle\">it.necst.grabnrun.<a href=\"it/necst/grabnrun/SecureLoaderFactory.html\" title=\"class in it.necst.grabnrun\"><span class=\"strong\">SecureLoaderFactory</span></a></li>\n</ul>\n</li>\n</ul>\n</div>\n<!-- ======= START OF BOTTOM NAVBAR ====== -->\n<div class=\"bottomNav\"><a name=\"navbar_bottom\">\n<!--   -->\n</a><a href=\"#skip-navbar_bottom\" title=\"Skip navigation links\"></a><a name=\"navbar_bottom_firstrow\">\n<!--   -->\n</a>\n<ul class=\"navList\" title=\"Navigation\">\n<li><a href=\"it/necst/grabnrun/package-summary.html\">Package</a></li>\n<li>Class</li>\n<li>Use</li>\n<li class=\"navBarCell1Rev\">Tree</li>\n<li><a href=\"deprecated-list.html\">Deprecated</a></li>\n<li><a href=\"index-files/index-1.html\">Index</a></li>\n<li><a href=\"help-doc.html\">Help</a></li>\n</ul>\n</div>\n<div class=\"subNav\">\n<ul class=\"navList\">\n<li>Prev</li>\n<li>Next</li>\n</ul>\n<ul class=\"navList\">\n<li><a href=\"index.html?overview-tree.html\" target=\"_top\">Frames</a></li>\n<li><a href=\"overview-tree.html\" target=\"_top\">No Frames</a></li>\n</ul>\n<ul class=\"navList\" id=\"allclasses_navbar_bottom\">\n<li><a href=\"allclasses-noframe.html\">All Classes</a></li>\n</ul>\n<div>\n<script type=\"text/javascript\"><!--\n  allClassesLink = document.getElementById(\"allclasses_navbar_bottom\");\n  if(window==top) {\n    allClassesLink.style.display = \"block\";\n  }\n  else {\n    allClassesLink.style.display = \"none\";\n  }\n  //-->\n</script>\n</div>\n<a name=\"skip-navbar_bottom\">\n<!--   -->\n</a></div>\n<!-- ======== END OF BOTTOM NAVBAR ======= -->\n</body>\n</html>\n"
  },
  {
    "path": "docs/javaDoc/package-list",
    "content": "it.necst.grabnrun\n"
  },
  {
    "path": "docs/javaDoc/stylesheet.css",
    "content": "/* Javadoc style sheet */\n/*\nOverall document style\n*/\nbody {\n    background-color:#ffffff;\n    color:#353833;\n    font-family:Arial, Helvetica, sans-serif;\n    font-size:76%;\n    margin:0;\n}\na:link, a:visited {\n    text-decoration:none;\n    color:#4c6b87;\n}\na:hover, a:focus {\n    text-decoration:none;\n    color:#bb7a2a;\n}\na:active {\n    text-decoration:none;\n    color:#4c6b87;\n}\na[name] {\n    color:#353833;\n}\na[name]:hover {\n    text-decoration:none;\n    color:#353833;\n}\npre {\n    font-size:1.3em;\n}\nh1 {\n    font-size:1.8em;\n}\nh2 {\n    font-size:1.5em;\n}\nh3 {\n    font-size:1.4em;\n}\nh4 {\n    font-size:1.3em;\n}\nh5 {\n    font-size:1.2em;\n}\nh6 {\n    font-size:1.1em;\n}\nul {\n    list-style-type:disc;\n}\ncode, tt {\n    font-size:1.2em;\n}\ndt code {\n    font-size:1.2em;\n}\ntable tr td dt code {\n    font-size:1.2em;\n    vertical-align:top;\n}\nsup {\n    font-size:.6em;\n}\n/*\nDocument title and Copyright styles\n*/\n.clear {\n    clear:both;\n    height:0px;\n    overflow:hidden;\n}\n.aboutLanguage {\n    float:right;\n    padding:0px 21px;\n    font-size:.8em;\n    z-index:200;\n    margin-top:-7px;\n}\n.legalCopy {\n    margin-left:.5em;\n}\n.bar a, .bar a:link, .bar a:visited, .bar a:active {\n    color:#FFFFFF;\n    text-decoration:none;\n}\n.bar a:hover, .bar a:focus {\n    color:#bb7a2a;\n}\n.tab {\n    background-color:#0066FF;\n    background-image:url(resources/titlebar.gif);\n    background-position:left top;\n    background-repeat:no-repeat;\n    color:#ffffff;\n    padding:8px;\n    width:5em;\n    font-weight:bold;\n}\n/*\nNavigation bar styles\n*/\n.bar {\n    background-image:url(resources/background.gif);\n    background-repeat:repeat-x;\n    color:#FFFFFF;\n    padding:.8em .5em .4em .8em;\n    height:auto;/*height:1.8em;*/\n    font-size:1em;\n    margin:0;\n}\n.topNav {\n    background-image:url(resources/background.gif);\n    background-repeat:repeat-x;\n    color:#FFFFFF;\n    float:left;\n    padding:0;\n    width:100%;\n    clear:right;\n    height:2.8em;\n    padding-top:10px;\n    overflow:hidden;\n}\n.bottomNav {\n    margin-top:10px;\n    background-image:url(resources/background.gif);\n    background-repeat:repeat-x;\n    color:#FFFFFF;\n    float:left;\n    padding:0;\n    width:100%;\n    clear:right;\n    height:2.8em;\n    padding-top:10px;\n    overflow:hidden;\n}\n.subNav {\n    background-color:#dee3e9;\n    border-bottom:1px solid #9eadc0;\n    float:left;\n    width:100%;\n    overflow:hidden;\n}\n.subNav div {\n    clear:left;\n    float:left;\n    padding:0 0 5px 6px;\n}\nul.navList, ul.subNavList {\n    float:left;\n    margin:0 25px 0 0;\n    padding:0;\n}\nul.navList li{\n    list-style:none;\n    float:left;\n    padding:3px 6px;\n}\nul.subNavList li{\n    list-style:none;\n    float:left;\n    font-size:90%;\n}\n.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {\n    color:#FFFFFF;\n    text-decoration:none;\n}\n.topNav a:hover, .bottomNav a:hover {\n    text-decoration:none;\n    color:#bb7a2a;\n}\n.navBarCell1Rev {\n    background-image:url(resources/tab.gif);\n    background-color:#a88834;\n    color:#FFFFFF;\n    margin: auto 5px;\n    border:1px solid #c9aa44;\n}\n/*\nPage header and footer styles\n*/\n.header, .footer {\n    clear:both;\n    margin:0 20px;\n    padding:5px 0 0 0;\n}\n.indexHeader {\n    margin:10px;\n    position:relative;\n}\n.indexHeader h1 {\n    font-size:1.3em;\n}\n.title {\n    color:#2c4557;\n    margin:10px 0;\n}\n.subTitle {\n    margin:5px 0 0 0;\n}\n.header ul {\n    margin:0 0 25px 0;\n    padding:0;\n}\n.footer ul {\n    margin:20px 0 5px 0;\n}\n.header ul li, .footer ul li {\n    list-style:none;\n    font-size:1.2em;\n}\n/*\nHeading styles\n*/\ndiv.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {\n    background-color:#dee3e9;\n    border-top:1px solid #9eadc0;\n    border-bottom:1px solid #9eadc0;\n    margin:0 0 6px -8px;\n    padding:2px 5px;\n}\nul.blockList ul.blockList ul.blockList li.blockList h3 {\n    background-color:#dee3e9;\n    border-top:1px solid #9eadc0;\n    border-bottom:1px solid #9eadc0;\n    margin:0 0 6px -8px;\n    padding:2px 5px;\n}\nul.blockList ul.blockList li.blockList h3 {\n    padding:0;\n    margin:15px 0;\n}\nul.blockList li.blockList h2 {\n    padding:0px 0 20px 0;\n}\n/*\nPage layout container styles\n*/\n.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {\n    clear:both;\n    padding:10px 20px;\n    position:relative;\n}\n.indexContainer {\n    margin:10px;\n    position:relative;\n    font-size:1.0em;\n}\n.indexContainer h2 {\n    font-size:1.1em;\n    padding:0 0 3px 0;\n}\n.indexContainer ul {\n    margin:0;\n    padding:0;\n}\n.indexContainer ul li {\n    list-style:none;\n}\n.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {\n    font-size:1.1em;\n    font-weight:bold;\n    margin:10px 0 0 0;\n    color:#4E4E4E;\n}\n.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {\n    margin:10px 0 10px 20px;\n}\n.serializedFormContainer dl.nameValue dt {\n    margin-left:1px;\n    font-size:1.1em;\n    display:inline;\n    font-weight:bold;\n}\n.serializedFormContainer dl.nameValue dd {\n    margin:0 0 0 1px;\n    font-size:1.1em;\n    display:inline;\n}\n/*\nList styles\n*/\nul.horizontal li {\n    display:inline;\n    font-size:0.9em;\n}\nul.inheritance {\n    margin:0;\n    padding:0;\n}\nul.inheritance li {\n    display:inline;\n    list-style:none;\n}\nul.inheritance li ul.inheritance {\n    margin-left:15px;\n    padding-left:15px;\n    padding-top:1px;\n}\nul.blockList, ul.blockListLast {\n    margin:10px 0 10px 0;\n    padding:0;\n}\nul.blockList li.blockList, ul.blockListLast li.blockList {\n    list-style:none;\n    margin-bottom:25px;\n}\nul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {\n    padding:0px 20px 5px 10px;\n    border:1px solid #9eadc0;\n    background-color:#f9f9f9;\n}\nul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {\n    padding:0 0 5px 8px;\n    background-color:#ffffff;\n    border:1px solid #9eadc0;\n    border-top:none;\n}\nul.blockList ul.blockList ul.blockList ul.blockList li.blockList {\n    margin-left:0;\n    padding-left:0;\n    padding-bottom:15px;\n    border:none;\n    border-bottom:1px solid #9eadc0;\n}\nul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {\n    list-style:none;\n    border-bottom:none;\n    padding-bottom:0;\n}\ntable tr td dl, table tr td dl dt, table tr td dl dd {\n    margin-top:0;\n    margin-bottom:1px;\n}\n/*\nTable styles\n*/\n.contentContainer table, .classUseContainer table, .constantValuesContainer table {\n    border-bottom:1px solid #9eadc0;\n    width:100%;\n}\n.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table {\n    width:100%;\n}\n.contentContainer .description table, .contentContainer .details table {\n    border-bottom:none;\n}\n.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{\n    vertical-align:top;\n    padding-right:20px;\n}\n.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast,\n.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast,\n.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne,\n.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne {\n    padding-right:3px;\n}\n.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption {\n    position:relative;\n    text-align:left;\n    background-repeat:no-repeat;\n    color:#FFFFFF;\n    font-weight:bold;\n    clear:none;\n    overflow:hidden;\n    padding:0px;\n    margin:0px;\n}\ncaption a:link, caption a:hover, caption a:active, caption a:visited {\n    color:#FFFFFF;\n}\n.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span {\n    white-space:nowrap;\n    padding-top:8px;\n    padding-left:8px;\n    display:block;\n    float:left;\n    background-image:url(resources/titlebar.gif);\n    height:18px;\n}\n.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd {\n    width:10px;\n    background-image:url(resources/titlebar_end.gif);\n    background-repeat:no-repeat;\n    background-position:top right;\n    position:relative;\n    float:left;\n}\nul.blockList ul.blockList li.blockList table {\n    margin:0 0 12px 0px;\n    width:100%;\n}\n.tableSubHeadingColor {\n    background-color: #EEEEFF;\n}\n.altColor {\n    background-color:#eeeeef;\n}\n.rowColor {\n    background-color:#ffffff;\n}\n.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td {\n    text-align:left;\n    padding:3px 3px 3px 7px;\n}\nth.colFirst, th.colLast, th.colOne, .constantValuesContainer th {\n    background:#dee3e9;\n    border-top:1px solid #9eadc0;\n    border-bottom:1px solid #9eadc0;\n    text-align:left;\n    padding:3px 3px 3px 7px;\n}\ntd.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {\n    font-weight:bold;\n}\ntd.colFirst, th.colFirst {\n    border-left:1px solid #9eadc0;\n    white-space:nowrap;\n}\ntd.colLast, th.colLast {\n    border-right:1px solid #9eadc0;\n}\ntd.colOne, th.colOne {\n    border-right:1px solid #9eadc0;\n    border-left:1px solid #9eadc0;\n}\ntable.overviewSummary  {\n    padding:0px;\n    margin-left:0px;\n}\ntable.overviewSummary td.colFirst, table.overviewSummary th.colFirst,\ntable.overviewSummary td.colOne, table.overviewSummary th.colOne {\n    width:25%;\n    vertical-align:middle;\n}\ntable.packageSummary td.colFirst, table.overviewSummary th.colFirst {\n    width:25%;\n    vertical-align:middle;\n}\n/*\nContent styles\n*/\n.description pre {\n    margin-top:0;\n}\n.deprecatedContent {\n    margin:0;\n    padding:10px 0;\n}\n.docSummary {\n    padding:0;\n}\n/*\nFormatting effect styles\n*/\n.sourceLineNo {\n    color:green;\n    padding:0 30px 0 0;\n}\nh1.hidden {\n    visibility:hidden;\n    overflow:hidden;\n    font-size:.9em;\n}\n.block {\n    display:block;\n    margin:3px 0 0 0;\n}\n.strong {\n    font-weight:bold;\n}\n"
  },
  {
    "path": "docs/repackaging.rst",
    "content": "\nRepackaging tool\n================\n\nIn this section you will learn how to configure and use the repackaging tool to have your applications rewritten automatically to use the Grab'n Run library.\nThe repackaging tool allows you to:\n\n* Rewrite your application to use Grab'n Run secure API instead of the standard Android ones. This process is automatic and requires only a couple of settings from your side. \n* Granular control on the security policy that you want your application to follow at run time when performing dynamic code loading (e.g., validate all the dynamically loaded code against one certificate, validate each container against a different certificate, decide whether to provide a default trusted certificate for all the not specified entries).\n\nThis tool relies on `Androguard <https://github.com/androguard/androguard>`_ to decoded, decompile, and rebuild the application provided as input.\n\nA prerequisite for using the tool is that your local machine must be able to have Internet connectivity since it will be necessary for the tool to download the source containers declared as potential sources for dynamic code loading unless they result directly accessible on the file-system of your machine.\n\nAnother prerequisite is that you have already successfully installed `apktool <http://ibotpeaches.github.io/Apktool/install/>`_ on your local machine.\n\nUse\n---\n\n.. highlight:: bash\n\n1. Open a terminal and move to the repackaging tool script *repackPOC* directory and then install the required dependencies::\n\n\t$ cd <absolute_path_to_GNR_folder>/repackPOC\n\t$ pip install -r requirements.txt\n\n2. Run the script::\n\n\t$ python repackagingTool.py\n\n3. For a list of the optional arguments::\n\n\t$ python repackagingTool.py -h\n\nConfiguration\n-------------\n\n.. highlight:: java\n\nBefore having the repackaging tool executed, you will have to go through a couple of windows to configure how you want your application to be patched. In particular:\n\n1. You have to select which application, in the form of an APK container, you want to patch.\n2. You have to provide the JAR or APK containers used as sources for dynamic code loading.\n3. You need to specify a security policy that will be used to evaluate whether the containers used as sources are genuine at runtime.\n\nYou can customize these settings easily thanks to a simple UI presented whenever the script is started.\n\nStep 1: Select the application to patch\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSelect the application you want to patch to use the secure Grab'n Run APIs. Notice that this application must be a regular APK file, which is stored locally on your machine.\n\nIt is not required for this container to be aligned, or signed, but notice that you will *have to* sign, and then align the final application generated by the script in order to have it installed on the user's devices.\n\nIf you are not familiar on how to *sign*, and *align* an APK, you may want to check the third bullet point of the \"Quick example of use\" section in the ``README`` file of the Grab'n Run repository on GitHub.\n\nStep 2: Select the containers sources for DCL\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNext step is listing all the JAR, or APK containers used by your application as sources for dynamic code loading. Each container can be either stored locally (in this case indicate the absolute path on the file system), or remotely on an endpoint (in this case simply type the URL pointing to the file, and notice that both HTTP and HTTPS protocol are supported).\n\n.. warning::\n\tIn case of a JAR contianer, verify that you have already translated its content into Dalvik bytecode. Containers that have been gone through this process present a\n\tspecific entry named ``classes.dex``. If you provide a container without such an entry, the repackaging tool will simply ignore it.\n\nThe screenshot below shows the first screen of the UI with an example of a feasible configuration for Step 1 and Step 2.\n\n.. image:: images/RepackWin1.png\n\nStep 3: Configure the security policy for the source containers\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOnce you confirm the settings in the first window, you will be prompted with a new screen to decide against which certificate each source container should be verified by Grab'n Run. At the moment we support three policies:\n\na. Evaluate all the containers against one certificate.\nb. Evaluate each container against a different certificate.\nc. Provide a validation mapping, which has on the left side the prefix of the package name of the involved classes, and on the right side the corresponding certificate to use for the evaluation.\n\nThe three screenshots below show an example of a valid configuration for each of the three policy. Switching from one to another is as simple as picking a different value in the drop-down menu box on top of the UI.\n\n.. image:: images/RepackWin2.png\n.. image:: images/RepackWin3.png\n.. image:: images/RepackWin4.png\n\nSome further considerations for this step:\n\n* You must provide each certificate through a remote reference to a secure endpoint containing such resource. The use of HTTPS protocol for the endpoint is required, and, if this is not the case, the repackaging tool will enforce it.\n* For the first two policies you can select as source containers either archives stored locally on your machine (use the absolute path of these resources on the file-system), or a remote endpoint containing such container (use its URL). In the latter case the repackaging tool will take care, when started, to retrieve these containers for further analysis. For these two policies, you can optionally also provide a default certificate that will be used for evaluating any other source container, which was not listed in the previous step but is used by your application at run time. \n* For the third policy the most restrictive package name prefix will be applied during the evaluation: This means that if your application wants to load code for a class, whose full name is ``com.example.myapp.MyClass``, and you have two entries in your mapping, one for ``com.example`` and the other for ``com.example.myapp``, the certificate associated to the latter one will be used since this entry has a longer prefix matching the name of the class to load. You should consider to use the last policy only if you are really aware of how the system works (see :doc:`tutorial`, and :doc:`complementary` for further details on the mapping process); in general, using one of the two first policies is already enough for most of the use cases, and it avoids pain from your side since the tool handles automatically the process of generating a validation mapping from your settings.\n\nStep 4: Gotta patch them all!\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nOnce you enter your settings and you press the \"*Finish*\" button, the repackaging tool will start its execution. If no error is raised, the patched APK will be available in the main folder where you launched the script from terminal (take extra care of having the original application in a different folder unless you want it to be overwritten by the patched version).\n\nI hope you will find this tool useful and I am eager to hear your feedbacks :)"
  },
  {
    "path": "docs/security.rst",
    "content": "Why should I use Grab'n Run?\n============================\n\nA significant question that every developer faces every time that (s)he adds a new library to his/her project is the following: \"*Is it worthy to add this library? Does it provide something really necessary to my project?*\"\n\nIn this specific case those two questions can be reformulated as follows:\n\"*Aren't Android API already able to handle dynamic class loading? Why should I use for example* ``SecureDexClassLoader`` *in stead of the regular* ``DexClassLoader`` *? Does it really enhance the standard class with something relevant?*\".\n\nTo answer all of these questions let us consider the case of choosing ``SecureDexClassLoader``, one of the classes provided by **Grab'n Run**, in stead of `DexClassLoader <http://developer.android.com/reference/dalvik/system/DexClassLoader.html>`_, the standard class provided by the Android API, to *dynamically load* **.dex classes** into your Android application at run time.\n\nFirst of all ``SecureDexClassLoader`` provides a couple of slight but significant **improvements** over the standard class in terms of **functionalities**:\n\n* ``SecureDexClassLoader`` lets you retrieve *dynamically* also classes from *.jar* and *.apk* containers which are **not located directly on the phone** running the application as long as you simply provide a **valid remote URL** for the resource, while ``DexClassLoader`` is only able to cache classes from containers stored on the phone.\n\n* ``SecureDexClassLoader`` is a **thread-safe** library and so, after that you have created your ``SecureDexClassLoader`` instance on the main thread of your application, you can launch different threads, each one performing dynamic class loading on the very same ``SecureDexClassLoader`` instance **without** incurring in nasty **race conditions** and **concurrencies exceptions**.\n\nIn addition and above all ``SecureDexClassLoader`` ensures relevant **security features** on classes that you dynamically load that are not possible to check or implemented with ``DexClassLoader`` like:\n\n* **Fetch remote code in a secure way**: ``SecureDexClassLoader`` retrieves remote code either via HTTP or HTTPS protocol. In both cases it **always verifies** and validates the downloaded containers **before actually loading classes** inside of them.\n\n* **Store containers in secure application-private locations**: `SecureDexClassLoader`` also **prevents** your application from being a possible target of **code injection attacks**. This attack becomes feasible whenever you use the standard ``DexClassLoader`` and you provide as an optimized cache folder for *dex* files a directory which is located in a **world writable area** of your phone (i.e. external storage) or you decide to load classes from a container which is, once again, stored in a world writable folder. ``SecureDexClassLoader`` on the other hand manages this situation for you by choosing *application-private directories* for caching *dex* files and storing containers and certificates. This strategy represents an **effective** way to make the **attack infeasible**.\n\n* **Developer authentication**: for each package containing classes to be loaded dynamically it is possible to ensure authentication of the developer who coded those classes though a *check of the signature on the container* of the classes against the *certificate of the developer* (which could possibly be even *self-signed*). **Non-signed** classes or those who were not signed by that required certificate will be rejected and **prevented from being loaded**.  \n\n* **Integrity**: during the *signature verification* process whenever one of the entries inside the container results to be *incorrectly signed*, ``SecureDexClassLoader`` recognized a **possible tampered or repackaged container** and it prevents your application from running the code of any classes inside this invalid and possible malicious container.\n\nAnd these improvements come with a **convenient overhead** on the **performance** :)\n\nThis is possible since ``SecureDexClassLoader`` was implemented with an accurate **caching system** that, from one side, *prevents* your application **from continuously downloading** the same *jar* and *apk* **containers and certificates** and, from the other side, **avoid** it from **verifying every time** *the signature and the integrity* of already **checked containers**.\n\nMoreover, for even *more performance concerned developers*, it is also possible to set the **strategy** which is going to be used by ``SecureDexClassLoader`` to **validate classes** before attempting to load them. In particular **two** options are provided:\n\n1. **Lazy Strategy**: this mode implies that the **signature and integrity** of each container will be **evaluated only when** the ``loadClass()`` method will be invoked on *one of the classes*, whose package name is linked to this container. An ideal case of use for this mode is when you have quite a lot of containers and just a couple of classes to load, which may also vary from one execution to another and so validating all the containers in this case may be a waste of time.\n\n2. **Eager Strategy**: in this mode the process of **signature and integrity** will be carried out on **all** the containers **immediately and concurrently** before returning an instance of ``SecureDexClassLoader``. This choice implies that you will have to pay an **initial penalty (but still reduced since the verification process is driven concurrently among the containers)** on time of execution but then the time required for a ``loadClass()`` operation becomes almost equal to the corresponding operation performed with standard ``DexClassLoader``.\n\n**By default eager strategy** is applied but developers can *pick* the *lazy version* by adding a final ``true`` attribute to the ``createDexClassLoader()`` method invocation\nin ``SecureLoaderFactory``. An example of use is shown in the following snippet of code (this is just a *slight modification* of one of the calls that you may have seen in :doc:`tutorial` )::\n\n\t\tSecureDexClassLoader mSecureDexClassLoader = \n\t\t\tmSecureLoaderFactory.createDexClassLoader(\tjarContainerPath, \n\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\n\t\t\t\t\t\t\t\t\tpackageNamesToCertMap,\n\t\t\t\t\t\t\t\t\ttrue);"
  },
  {
    "path": "docs/tutorial.rst",
    "content": "\nQuick start and tutorial\n========================\n\nIn this section you will see how to *retrieve and include Grab'n Run library* into your project (either by using Android Studio ot the Android Development Tool). After this setup step a **brief tutorial** will explain how to use classes in the library to **secure** the *dynamic code loading operations*.\n\nSince this section is **introductory** and more descriptive, it should be read by those who are not familiar with this library or more in general with *class loading* in Android. On the other hand the :doc:`complementary` section provides a more complete and detailed view on *Grab'n Run* library and its insights, while :doc:`example` shows a simple use case of the concepts introduced here.\n\nQuick Setup\n-----------\n\nSetting up GNR as an **additional library** for your *Android application* is very easy:\n\nAndroid Studio (AS)\n~~~~~~~~~~~~~~~~~~~\n..\thighlight:: groovy\n\n1. Modify the *build.gradle* file in the *app* module of your Android project by adding the following *compile* line in the *dependencies* body::\n\n\tdependencies {\n\t\t// Grab'n Run will be imported from JCenter.\n\t\t// Verify that the string \"jcenter()\" is included in your repositories block!\n\t\tcompile 'it.necst.grabnrun:grabnrun:1.0.4'\n\t}\n\n2. Resync your project to apply changes.\n\nAndroid Development Tool (ADT)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n1. Download the latest `version <https://github.com/lukeFalsina/Grab-n-Run/raw/master/downloads/1.0.4/grabnrun-1.0.4.jar>`_ of the *JAR* container of Grab'n Run.\n\n2. Include the *JAR* in the **libs** subfolder of your Android project.\n\nAdding missing permissions (Both AS and ADT)\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n..\thighlight:: xml\n\nFinally it is required to modify the *Android Manifest* of your application by adding a couple of required permissions if they are not already in place::\n\n\t<manifest>\n\t\t<!-- \tInclude following permission to be able to download remote resources \n\t\t\tlike containers and certificates -->\n\t\t<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\t\t<!-- \tInclude following permission to be able to download remote resources \n\t\t\tlike containers and certificates -->\n\t\t<uses-permission android:name=\"android.permission.INTERNET\" />\n\t\t<!-- \tInclude following permission to be able to import local containers \n\t\t\ton SD card -->\n\t\t<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n\t\t...\n\t</manifest>\n\n..\thighlight:: java\n\nTutorial\n--------\n\nThis tutorial assumes that you have **already retrieved Grab'n Run and linked it** to one of your existing Android projects.\n\nUsing standard DexClassLoader to load code dynamically\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nLet us pretend that you want to dynamically load an external class through `DexClassLoader <http://developer.android.com/reference/dalvik/system/DexClassLoader.html>`_, a class in the *Android API* used to load classes from *jar* and *apk* files containing a **classes.dex** entry. This is a convenient way to execute code not installed as part of an application package.\n\nLet's assume, for example, that you want to load an instance of ``com.example.MyClass`` located in the container *exampleJar.jar*, stored in the *Download* folder of the sd_card on the target phone. Note that this scenario may potentially lead to a **code injection** attack when you use the standard ``DexClassLoader`` since you are choosing to load code from a container which is stored in a **world writable location** of your phone. Notice that this kind of attack would be prevented with ``SecureDexClassLoader``.\nAnyway a snippet of code to achieve this task is the following::\n\n\t\tMyClass myClassInstance = null;\n\t\tString jarContainerPath = \tEnvironment.getExternalStorageDirectory().getAbsolutePath() \n\t\t\t\t\t\t+ \"/Download/exampleJar.jar\";\n\t\tFile dexOutputDir = getDir(\"dex\", MODE_PRIVATE);\n\t\tDexClassLoader mDexClassLoader = new DexClassLoader(\tjarContainerPath, \n\t\t\t\t\t\t\t\t\tdexOutputDir.getAbsolutePath(), \n\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\tgetClass().getClassLoader());\n\t\t\n\t\ttry {\n\t\t\tClass<?> loadedClass = mDexClassLoader.loadClass(\"com.example.MyClass\");\n\t\t\tmyClassInstance = (MyClass) loadedClass.newInstance();\n\n\t\t\t// Do something with the loaded object myClassInstance\n\t\t\t// i.e. myClassInstance.doSomething();\n\n\t\t} catch (ClassNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\nThe String ``jarContainerPath`` contains the path to *examplejar*, while ``dexOutputDir`` is an **application-private**, writable directory to cache optimized *dex* classes into *examplejar*. As reported in ``DexClassLoader`` documentation, you can retrieve ``dexOutputDir`` in different ways but it is fundamental that this cache folder is application-private; otherwise your application may be subjected to **code injection attacks**. And by the way this kind of attack is *prevented* if you choose to use ``SecureDexClassLoader`` as explained later on in this guide.\n\nThe object ``mDexClassLoader`` is then initialized as a ``DexClassLoader`` instance, which loads all the classes\ninto *examplejar* and caches their optimized version into ``dexOutputDir``. No native library is included\nsince the third parameter of the constructor is ``null`` and the `ClassLoader <http://developer.android.com/reference/java/lang/ClassLoader.html>`_ of the current activity is passed as parent class loader.\n\nFinally the designated class is, at first, loaded by invoking the ``loadClass()`` method on ``mDexClassLoader`` with the **full class name** provided as a parameter and, secondly, instantiated through the ``newInstance()`` method and the forced\ncasting to ``MyClass``. The three different **catch blocks** are used to handle different exceptions that may be raised during the process.\n\n**Package Name**\n\tIn Java every class is associated to a **package name**. A **package** is a *grouping of related classes, interfaces and enumerations* providing **access protection** and **name space management**. In particular in *Grab'n Run* packages names are accepted if and only if they are *a sequence of at least two not-empty, dot-separated words, which starts and ends with a word, not with a dot*. This implies that the following are all examples of **invalid** package names: ``com``, ``.com.application``, ``com..application``, ``com.application.``, while **suitable** package names are ``com.application`` or ``it.polimi.necst.gnr``. As you will see later on in this tutorial, package names perform a **relevant functionality** in **GNR** system since they *link containers to be verified with the certificate used to do so*.\n\n.. warning::\n\tNotice that a **full class name** is required to successfully load a class and so the **complete package name** separated by dots must **precede** the **class name**.\n\tReferred to the example, full class name is ``com.example.MyClass`` and not just the short class name ``MyClass``, which would produce a failure in the class loading operation.\n\tIn particular if it is the case that a short class name is provided in stead of a full one, it is likely that a ``ClassNotFoundException`` will be raised at runtime.\n\nThis snippet of code is perfectly fine and working but it is **not completely secure** since neither integrity on the container of the classes, neither authentication on the developer of the container are checked before executing the code.\nAnd here comes ``SecureDexClassLoader`` to solve these issues.  \n\n.. _Using SecureDexClassLoader to load dynamic code securely:\n\nUsing SecureDexClassLoader to load dynamic code securely \n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn order to improve the security of the snippet of code shown in `Using standard DexClassLoader to load code dynamically`_\na new version of the code is presented through the use of ``SecureDexClassLoader`` and ``SecureLoaderFactory``.\n\nAt first you should create a ``SecureLoaderFactory`` object as shown here::\n\n\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\nThis is an helper class necessary to generate a ``SecureDexClassLoader`` object.\nBut before performing this step you have to initialize and provide to ``mSecureLoaderFactory`` an **associative map** \nwhich links all the package names of the classes that you want to dynamically load to one *developer certificate*,\nwhich is stored at a **secure web location** (i.e. an HTTPS link) and which was previously used \nto sign the *jar* or *apk* container which holds those classes.\n\n**Developer Certificate**\n\ta certificate, which in Android can be even *self-signed*, used to sign all the entries\n\tcontained in a *jar* or in an *apk* container. Notice that in the Android environment in order to run \n\tan application on a smart phone or to publish it on a store, the *signing step* is **mandatory** and can be \n\tused to check that an *apk* was actually written and approved by the issuer of the certificate.\n\tFor more details on signing applications and certificate, please check `here <http://developer.android.com/tools/publishing/app-signing.html#cert>`_.\n\nSo in this example we assume that all the classes belonging to the package ``com.example`` have been signed \nwith a self-signed certificate, stored at ``https://something.com/example_cert.pem``.\nSince here you just want to load ``com.example.MyClass`` the following snippet of code is enough::\n\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\t\ttry {\n\t\t\tpackageNamesToCertMap.put(\t\"com.example\",\n\t\t\t\t\t\t\tnew URL(\"https://something.com/example_cert.pem\"));\n\n\t\t} catch (MalformedURLException e) {\n\t\t\t// The previous URL used for the packageNamesToCertMap entry was a malformed one.\n\t\t\tLog.e(\"Error\", \"A malformed URL was provided for a remote certificate location\");\n\t\t}\n\t\t\n\n.. note::\n\tAny *self-signed certificate* can be used to validate classes to load as long as it is not \n\texpired and it suits the standard `X509 Certificate <http://docs.oracle.com/javase/7/docs/api/java/security/cert/X509Certificate.html>`_ format. The only exception is\n\trepresented by the **Android Debug Certificate**, a certificate used to sign applications before\n\trunning them in debug mode and not safe to use during production phase.\n\t``SecureDexClassLoader`` has been instructed to automatically reject class loading for classes \n\twhose package name has been associated for signature verification to the **Android Debug Certificate** \n\tand so **DO NOT USE IT** to check the signature of your containers.\n\n.. note::\n\tYou may want to insert more than one entry into the associative map. This is useful whenever you want to\n\tuse the same ``SecureDexClassLoader`` to load classes which belong to different packages. Still \n\tremember that each package name can only be associated with **one and only one** certificate location.\n\tPushing into the associative map an entry with an already existing package name will simply overwrite \n\tthe previously chosen location of the certificate for that package name.\n\n.. warning::\n\tFor each entry of the map only an **HTTPS** link will be accepted. This is necessary in order to \n\t**avoid MITM (Man-In-The-Middle)** attacks while retrieving the *trusted* certificate. In case that an **HTTP**\n\tlink is inserted, ``SecureLoaderFactory`` will enforce *HTTPS protocol* on it and in any case whenever \n\tno certificate is found at the provided URL, no dynamic class loading will succeed for any class of \n\tthe related package so **take care to verify** that certificate URL is correctly spelled and working via **HTTPS** protocol.\n\nNow it comes the time to initialize a ``SecureDexClassLoader`` object through the method ``createDexClassLoader()``\nof ``SecureLoaderFactory``::\n\n\t\tSecureDexClassLoader mSecureDexClassLoader = \n\t\t\tmSecureLoaderFactory.createDexClassLoader(\tjarContainerPath, \n\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\n\t\t\t\t\t\t\t\t\tpackageNamesToCertMap);\n\n``mSecureDexClassLoader`` will be able to load the classes whose container path is listed in ``jarContainerPath`` and \nit will use the ``packageNamesToCertMap`` to retrieve all the required certificate from the web and import them into \nan application private certificate folder. Also notice that in this case no directory to cache output classes is needed\nsince ``SecureDexClassLoader`` will automatically reserve such a folder.\n\n.. warning::\n\tAs stated in the `API documentation <http://developer.android.com/reference/dalvik/system/DexClassLoader.html#DexClassLoader(java.lang.String, java.lang.String, java.lang.String, java.lang.ClassLoader)>`_ ``jarContainerPath`` may link many *different containers* separated by ``:`` and \n\tfor such a reason the **developer is responsible** of filling the associative map of the certificates location\n\taccordingly with all the entries needed to cover all the package names of the classes to be loaded.\n\n.. note::\n\t``DexClassLoader``, the standard class from Android API, is able to parse and import only those *jar* and *apk* \n\tcontainers listed in ``jarContainerPath`` which are directly saved on the mobile device storage. In addition to this \n\t``SecureDexClassLoader`` is also capable of **downloading remote containers** from the web \n\t(i.e. **HTTP or HTTPS URL**) and to import them into an application-private directory to avoid code injections \n\tfrom attackers.\n\t\n\tExample::\n\n\t\tjarContainerPath = \"http://something.com/dev/exampleJar.jar\";\n\n\tThis ``jarContainerPath`` will retrieve no resource when used in the constructor of ``DexClassLoader`` but it \n\tis perfectly fine as a first parameter of the ``mSecureLoaderFactory.createDexClassLoader()`` call, as long as\n\ta *jar* container is actually stored at the remote location.\n\nFinally you can use the resulting ``mSecureDexClassLoader`` to load the desired class in a similar fashion to ``DexClassLoader``::\n\n\t \ttry {\n\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(\"com.example.MyClass\");\n\n\t\t\t// Check whether the signature verification process succeeds\n\t\t\tif (loadedClass == null) {\n\n\t\t\t\t// One of the security constraints was violated so no class\n\t\t\t\t// loading was allowed..\n\t\t\t}\n\t\t\telse {\n\n\t\t\t\t// Class loading was successful and performed in a safe way.\n\t\t\t\tmyClassInstance = (MyClass) loadedClass.newInstance();\n\n\t\t\t\t// Do something with the loaded object myClassInstance\n\t\t\t\t// i.e. myClassInstance.doSomething();\n\t\t\t}\n\n\t\t} catch (ClassNotFoundException e) {\n\t\t\t// This exception will be raised when the container of the target class\n\t\t\t// is genuine but this class file is missing..\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\nIt is important to notice that, differently from ``DexClassLoader``, the ``mSecureDexClassLoader.loadClass()`` call will \nreturn ``null``  whenever **at least one of the following security constraints is violated**:\n\n* The *package name* of the class used as a parameter of ``loadClass()`` was **not previously included in the associative\n  map** and so it do not exist any certificate that could be used to validate this class.\n* The *package name* of the class used as a parameter of ``loadClass()`` was previously included in the associative map\n  but the **related certificate** was **not found** (URL with no certificate file attached or no connectivity) or **not valid** \n  (i.e. expired certificate, use of the Android Debug Certificate).\n* The *container file* of the required class was **not signed**.\n* The *container file* of the required class was **not signed with the certificate associated** to the package name \n  of the class. [Missing trusted certificate]\n* At least one of the **entry** of the *container file* do **not match its signature** even if the certificate used to sign\n  the container file is the trusted one. [Possibility of **repackaged container**]\n\nFor all of these reasons you should always check and pay attention when a **null** pointer is returned after a \n``mSecureDexClassLoader.loadClass()`` call since this is a clear clue to establish either a wrong set up of \n``SecureLoaderFactoty`` and ``SecureDexClassLoader`` or a security violation. \n*Informative and debug messages* will be generated in the logs by the classes of the Grab'n Run library in order \nto help you figure out what it is happening.\n\n.. note::\n\tEvery time that ``SecureDexClassLoader`` finds out a (possibly repackaged) **invalid container**, it will immediately \n\t**delete** this file from its **application-private directory**. Nevertheless if this container is *stored on your device* \n\tit may be a good idea for you, as a developer, after having double checked that you have properly set up ``SecureDexClassLoader``, \n\tto **look for a fresh copy** of the container or at least **not to trust** and delete this container from the phone.\n\nPlease notice, on the other hand, that the three exceptions caught in the try-catch block surrounding the ``loadClass()`` method \nbehaves and are thrown in the same way as it would happen with ``DexClassLoader``.\n\nFinally for clarity the **full snippet of code** presented in this section is reported here::\n\n\t\tMyClass myClassInstance = null;\n\t\tjarContainerPath = \"http://something.com/dev/exampleJar.jar\";\n\n\t\ttry {\n\t\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\t\t\tpackageNamesToCertMap.put(\t\"com.example\",\n\t\t\t\t\t\t\tnew URL(\"https://something.com/example_cert.pem\"));\n\n\t\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\t\t\tSecureDexClassLoader mSecureDexClassLoader = \n\t\t\t\tmSecureLoaderFactory.createDexClassLoader(\tjarContainerPath, \n\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\n\t\t\t\t\t\t\t\t\t\tpackageNamesToCertMap);\n\t\t\n\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(\"com.example.MyClass\");\n\n\t\t\t// Check whether the signature verification process succeeds\n\t\t\tif (loadedClass == null) {\n\n\t\t\t\t// One of the security constraints was violated so no class\n\t\t\t\t// loading was allowed..\n\t\t\t}\n\t\t\telse {\n\n\t\t\t\t// Class loading was successful and performed in a safe way.\n\t\t\t\tmyClassInstance = (MyClass) loadedClass.newInstance();\n\t\t\t\t\n\t\t\t\t// Do something with the loaded object myClassInstance\n\t\t\t\t// i.e. myClassInstance.doSomething();\n\t\t\t}\n\n\t\t} catch (ClassNotFoundException e) {\n\t\t\t// This exception will be raised when the container of the target class\n\t\t\t// is genuine but this class file is missing..\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (MalformedURLException e) {\n\t\t\t// The previous URL used for the packageNamesToCertMap entry was a malformed one.\n\t\t\tLog.e(\"Error\", \"A malformed URL was provided for a remote certificate location\");\n\t\t}\n\n\nWiping out cached containers and certificates\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIn order to *improve performance* and offer the possibility to *partially work also when connectivity is limited*, \n``SecureDexClassLoader`` will store certificates retrieved from the web and all containers into specific **application-private directories**.\n\nEvery time that a **resource** (container or certificate) is needed to load or verify a class, ``SecureDexClassLoader`` will at first \nlook for it inside its private directories and then, if no match is found, possibly attempt to download it from the web or found it \nat a specified location on the device (this last option is applicable only for containers).\n\n.. It was also stated into `Using SecureDexClassLoader to load dynamic code securely`_ that, differently from\n.. ``DexClassLoader``, ``SecureDexClassLoader`` is also able to **download and import remote containers** into an\n.. *application-private folder*.\n\nEven if these **caching features** may come really useful and *speed up* significantly ``SecureDexClassLoader`` execution,\nit would be also nice for the developer to have the possibility to **choose** whether a **fresh or cached copy** of either a \ncertificate or a container should be used for the *dynamic loading operations*. And that is the reason why ``SecureDexClassLoader``\nprovides a method called ``wipeOutPrivateAppCachedData()`` to manage this choice.\n\nTo present this method let us consider again the previous scenario shown in `Using SecureDexClassLoader to load dynamic code securely`_: \nafter having tried to load ``com.example.MyClass``, if you want to *delete both the cached certificates and the containers* used by the \nrelated ``mSecureDexClassLoader``, in order to impose for the next loading operation the retrieval of **fresh resources**, the call to \nperform is the following::\n\n\t\tmSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\n.. warning::\n\tAfter that you *have erased at least one cached resource between the certificates and the containers*, ``mSecureDexClassLoader``\n\twill always return ``null`` for **consistency reason** to any invocation of the ``loadClass()`` method. \n\tSo it will be **necessary** for you to require a **new** ``SecureDexClassLoader`` instance to ``SecureLoaderFactory``\n\tthrough the invocation of the ``createDexClassLoader()`` method before being able to dynamically and securely load other classes.\n"
  },
  {
    "path": "downloads/1.0/gnr-1.0.jar.sha1",
    "content": "26de8ca9e32987d5d5094c432ad7480b09b134b4  gnr-1.0.jar\n"
  },
  {
    "path": "downloads/1.0.1/gnr-1.0.1.aar.sha1",
    "content": "10e18e6c5e14ebfbb075a746334d1722040c59f6  gnr-1.0.1.aar\n"
  },
  {
    "path": "downloads/1.0.1/gnr-1.0.1.jar.sha1",
    "content": "c56ebdfc50ae227aa85f0aaf2d4559dadce076e3  gnr-1.0.1.jar\n"
  },
  {
    "path": "downloads/1.0.2/gnr-1.0.2.aar.sha1",
    "content": "2d1d9bc079ae9908ce1f7b8c826ee8f0657aa40b  gnr-1.0.2.aar\n"
  },
  {
    "path": "downloads/1.0.2/gnr-1.0.2.jar.sha1",
    "content": "330385bd0e67a8b0b83048577d995c916aae7de2  gnr-1.0.2.jar\n"
  },
  {
    "path": "downloads/1.0.3/grabnrun-1.0.3.aar.sha1",
    "content": "b6b10ea1c67856374830a07080a02dcbe973ba6d"
  },
  {
    "path": "downloads/1.0.3/grabnrun-1.0.3.jar.sha1",
    "content": "8802180428e8783339a77484bfa45a74021c5b54  grabnrun-1.0.3.jar\n"
  },
  {
    "path": "downloads/1.0.4/grabnrun-1.0.4.aar.sha1",
    "content": "cc06e36eeb7104b5cc0fa555c668990b027bf53a"
  },
  {
    "path": "downloads/1.0.4/grabnrun-1.0.4.jar.sha1",
    "content": "9e849b9adbf0ca3c48ac8d71704753a885a8ede2  grabnrun-1.0.4.jar\n"
  },
  {
    "path": "example/ADT/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"src\" path=\"gen\"/>\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n</classpath>\n"
  },
  {
    "path": "example/ADT/.gitignore",
    "content": "/.settings/\n/bin/\n/gen/\n"
  },
  {
    "path": "example/ADT/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>ExampleAppGNR</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.ApkBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>com.android.ide.eclipse.adt.AndroidNature</nature>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "example/ADT/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"it.polimi.poccodeloading\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"21\" />\n    \n    <uses-permission android:name=\"android.permission.INTERNET\"></uses-permission>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"></uses-permission>\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"></uses-permission>\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\" >\n        <activity\n            android:name=\"it.polimi.poccodeloading.MainActivity\"\n            android:screenOrientation=\"landscape\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize\"\n            android:label=\"@string/app_name\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"it.polimi.poccodeloading.DexClassSampleActivity\"\n            android:label=\"@string/title_activity_dex_class_sample\" \n            android:parentActivityName=\"it.polimi.poccodeloading.MainActivity\"\n            android:screenOrientation=\"landscape\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize\" >\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "example/ADT/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "example/ADT/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "example/ADT/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-19\nandroid.library=false\n"
  },
  {
    "path": "example/ADT/res/layout/activity_dex_class_sample.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\"\n    tools:context=\"${packageName}.${activityClass}\" >\n\n    <Switch\n        android:id=\"@+id/switch_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\"\n        android:text=\"@string/switch_dex_load_jar\" />\n\n    <TextView\n        android:id=\"@+id/exp_text_dex_load_jar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/explanation_dex_load_jar\"\n        android:gravity=\"center\"\n        android:textSize=\"20sp\" />\n    \n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n    \tandroid:layout_height=\"wrap_content\"\n    \tandroid:gravity=\"center\"\n    \tandroid:orientation=\"horizontal\"\n    \tstyle=\"?android:attr/buttonBarStyle\" >\n        \n        <Button\n        android:id=\"@+id/button1_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onBtnClick\"\n        android:text=\"@string/btn_dex_load_jar\" />\n        \n        <Button\n        android:id=\"@+id/button2_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onBtnClick\"\n        android:text=\"@string/btn_dex_load_jar\" />\n        \n        <Button\n        android:id=\"@+id/button3_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onBtnClickExit\"\n        android:visibility=\"gone\"\n        android:text=\"@string/btn_dex_load_jar\" />\n        \n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "example/ADT/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\"\n    android:gravity=\"center\"\n\tandroid:layout_weight=\"0\"\n    tools:context=\".MainActivity\" >\n\n        <TextView \n\t   \tandroid:id=\"@+id/application_aim\"\n   \t    android:layout_width=\"match_parent\"\n    \tandroid:layout_height=\"wrap_content\"\n    \tandroid:gravity=\"center\"\n    \tandroid:text=\"@string/app_aim\"\n    \tandroid:textSize=\"16sp\"\n\t\t/>\n    \t\n\t\t<ListView\n    \tandroid:id=\"@+id/listview\"\n    \tandroid:layout_width=\"fill_parent\" \n    \tandroid:layout_height=\"wrap_content\"\n\t\t/>\n\n</LinearLayout>"
  },
  {
    "path": "example/ADT/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">Dynamic Code Loading Techniques</string>\n    <string name=\"app_aim\">The aim of this simple application\n        is showing a couple of techniques which could be used\n        to load external code from a remote or a local path\n        dynamically.</string>\n    <string name=\"title_activity_dex_class_sample\">DexClassLoader with a .jar container</string>\n    <string name=\"explanation_dex_load_jar\">Here it is shown a small example\n        of a Java class, loaded dynamically through DexClassLoader from a jar container.\n        \\nIts methods are used to rearrange and modify the graphical components in this screen..\n        \\nGuess what you should do now ;)</string>\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"btn_dex_load_jar\">Click me!</string>\n    <string name=\"switch_dex_load_jar\">Switch</string>\n\n</resources>\n"
  },
  {
    "path": "example/ADT/src/it/polimi/poccodeloading/ComponentModifier.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.polimi.poccodeloading;\n\nimport java.util.List;\n\nimport android.widget.Button;\nimport android.widget.Switch;\nimport android.widget.TextView;\n\n/**\n * Interface defined to mask the dynamic retrieval of the customizer\n * used to modify the layout of GUI components.\n * \n * @author Luca Falsina\n *\n */\npublic interface ComponentModifier {\n\t\n\tpublic void customizeButtons(List<Button> buttonList);\n\t\n\tpublic void customizeSwitch(Switch switchSlider);\n\n\tpublic void customizeTextView(TextView textView);\n\n}\n"
  },
  {
    "path": "example/ADT/src/it/polimi/poccodeloading/DexClassSampleActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.polimi.poccodeloading;\n\nimport it.necst.grabnrun.SecureDexClassLoader;\nimport it.necst.grabnrun.SecureLoaderFactory;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport dalvik.system.DexClassLoader;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.CompoundButton;\nimport android.widget.CompoundButton.OnCheckedChangeListener;\nimport android.widget.Switch;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\n/**\n * This activity shows a small example in which a customizer element is loaded\n * dynamically at run time through a DexClassLoader API call.\n * Depending on the provided jar container this object will customize in a \n * different way the layout of the components in the GUI even if the same \n * method calls are executed.\n * \n * @author Luca Falsina\n * \n */\npublic class DexClassSampleActivity extends Activity {\n\n\t// Unique identifier used for Log entries\n\tprivate static final String TAG_DEX_SAMPLE = DexClassSampleActivity.class.getSimpleName();\n\t\n\tprivate boolean isSecureModeChosen;\n\t\n\t// This is the interface used to mask the object\n\t// retrieved from the external jar..\n\tprivate ComponentModifier mComponentModifier;\n\t\n\t//private String assetSuffix = \"/exampleJar/componentModifier.jar\";\n\t\n\t// Used to pick different classes dynamically at run time\n\tprivate final String firstClassName = \"it.polimi.componentmodifier.FirstComponentModifierImpl\";\n\tprivate final String secondClassName = \"it.polimi.componentmodifier.SecondComponentModifierImpl\";\n\t\n\t// Used to visualize helper toast messages..\n\tprivate Handler toastHandler;\n\t\n\t// Components of the layout\n\tprivate TextView textView;\n\tprivate Button firstBtn, secondBtn, thirdBtn;\n\tprivate Switch switchSlider;\n\t\n\t// Path where \"componentModifier.jar\" and its repackaged version are stored on the device.\n\tprivate String jarContainerPath, jarContainerRepackPath;\n\t\n\t// Initialized only if the secure mode is enabled..\n\tprivate SecureDexClassLoader mSecureDexClassLoader;\n\t\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_dex_class_sample);\n\t\tsetTitle(getString(R.string.title_activity_dex_class_sample));\n\t\t\n\t\ttoastHandler = new Handler();\n\t\t\n\t\t// Retrieve the intent of the launching activity\n\t\tIntent intent = getIntent();\n\t\t\n\t\t// Enable/Disable secure loading;\n\t\tisSecureModeChosen = intent.getBooleanExtra(MainActivity.IS_SECURE_LOADING_CHOSEN, false);\n\t\t\n\t\tmSecureDexClassLoader = null;\n\t\t\n\t\t// final String jarContainerPath = getAssets() + assetSuffix;\n\t\tjarContainerPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/componentModifier.jar\";\n\t\t// final String jarContainerPath = \"https://github.com/lukeFalsina/test/blob/master/componentModifier.jar\";\n\t\t//jarContainerRepackPath = \"https://dl.dropboxusercontent.com/u/28681922/componentModifierRepack.jar\";\n\t\tjarContainerRepackPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/componentModifierRepack.jar\";\n\n\t\t// Retrieve all the components, which are going to be modified\n\t\t// by the instance of ComponentModifier\n\t\ttextView = (TextView) findViewById(R.id.exp_text_dex_load_jar);\n\t\tfirstBtn = (Button) findViewById(R.id.button1_dex_load_jar);\n\t\tsecondBtn = (Button) findViewById(R.id.button2_dex_load_jar);\n\t\tthirdBtn = (Button) findViewById(R.id.button3_dex_load_jar);\n\t\tswitchSlider = (Switch) findViewById(R.id.switch_dex_load_jar);\n\t\tswitchSlider.setOnCheckedChangeListener(new OnCheckedChangeListener() {\n\n\t\t\t   @Override\n\t\t\t   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {\n\n\t\t\t\t   if(isChecked){\n\t\t\t\t\t   onBtnClickExit(buttonView);\n\t\t\t\t   }\n\t\t\t   }\n\t\t});\n\t\t\n\t}\n\t\n\t// A simple method that randomly assign the path container of either the\n\t// original and benign jar container or the repackaged one.\n\tprivate String randomContainerPathChoice() {\n\t\t\n\t\tRandom randomBooleanGen = new Random();\n\t\tboolean randomBoolean = randomBooleanGen.nextBoolean();\n\t\t\n\t\tif (!randomBoolean) return jarContainerPath;\n\t\telse return jarContainerRepackPath;\n\t}\n\t\n\tprivate ComponentModifier retrieveComponentModifier(String className) {\n\n\t\tLog.d(TAG_DEX_SAMPLE, \"Setting up Dex Class Loader..\");\n\t\t\n\t\tComponentModifier retComponentModifier = null;\n\t\t\n\t\tfinal String jarContainerChoicePath = randomContainerPathChoice();\n\t\t\n\t\tFile dexOutputDir = getDir(\"dex\", MODE_PRIVATE);\n\t\t\n\t\tDexClassLoader mDexClassLoader = new DexClassLoader(\tjarContainerChoicePath, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdexOutputDir.getAbsolutePath(), \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tgetClass().getClassLoader());\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\t// It does not matter which one of the two containers is selected..\n\t\t\t// DexClassLoader will always return a class from either the benign or the\n\t\t\t// repackaged container..\n\t\t\tClass<?> loadedClass = mDexClassLoader.loadClass(className);\n\t\t\t\n\t\t\tretComponentModifier = (ComponentModifier) loadedClass.newInstance();\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Class not found!\");\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Instantiation issues!\");\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Illegal access!\");\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\tif (retComponentModifier != null) {\n\t\t\t\n\t\t\tfinal String shortClassName = retComponentModifier.getClass().getSimpleName();\n\t\t\t\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"DexClassLoader was successful!\\nLoaded class name:\" + shortClassName + \"\\nPath: \" + jarContainerChoicePath);\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"DexClassLoader was successful!\\nLoaded class name: \" + shortClassName + \"\\nPath: \" + jarContainerChoicePath,\n\t\t\t\t\t\t\tToast.LENGTH_LONG).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"DexClassLoader failed!\\nLeaving this activity..\",\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\t// Exit this activity..\n\t\t\tfinish();\n\t\t}\n\t\t\n\t\treturn retComponentModifier;\n\t}\n\t\n\tprivate ComponentModifier retrieveComponentModifierSecurely(String className) {\n\t\t\n\t\tLog.d(TAG_DEX_SAMPLE, \"Setting up SecureDexClassLoader..\");\n\t\t\n\t\tComponentModifier retComponentModifier = null;\n\t\t\n\t\tfinal String jarContainerChoicePath = randomContainerPathChoice();\n\t\t\n\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\t\t\n\t\t// Filling the associative map to link package names and certificates..\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\t\t// 1st Entry: valid remote certificate location\n\t\ttry {\n\t\t\tpackageNamesToCertMap.put(\"it.polimi.componentmodifier\", new URL(\"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\"));\n\t\t} catch (MalformedURLException e1) {\n\t\t\t// A malformed URL was used for remote certificate location\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"A malformed URL was used for remote certificate location!\");\n\t\t}\n\t\t\n\t\t// Initialize SecureDexClassLoader with repackaged jar container..\n\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\tjarContainerChoicePath, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpackageNamesToCertMap);\n\t\t\n\t\t// Class returned from the loadClass() operation..\n\t\tClass<?> loadedClass = null;\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\tloadedClass = mSecureDexClassLoader.loadClass(className);\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Class not found!\");\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\tif (loadedClass != null) {\n\t\t\t\n\t\t\t// In this scenario the provided container is a repackaged version of the benign one\n\t\t\t// so if this branch is accessed, it means that SecureDexClassLoader failed in\n\t\t\t// the signature verification step in loadClass() method.\n\t\t\ttry {\n\t\t\t\tretComponentModifier = (ComponentModifier) loadedClass.newInstance();\n\t\t\t} catch (InstantiationException e) {\n\t\t\t\tLog.e(TAG_DEX_SAMPLE, \"Problem in the instantiation of the target class!\");\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (IllegalAccessException e) {\n\t\t\t\tLog.e(TAG_DEX_SAMPLE, \"Problem in the access to the target class!\");\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\t\n\t\t\tfinal String shortClassName = retComponentModifier.getClass().getSimpleName();\n\t\t\t\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"SecureDexClassLoader found out a consistent container!\\nLoaded class name:\" + shortClassName + \"\\nPath: \" + jarContainerChoicePath);\n\t\t\t\n\t\t\t// Erase all the cached resources..\n\t\t\t// mSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"SecureDexClassLoader found out a valid ComponentModifier container!\\nLoaded class name: \" + shortClassName + \"\\nPath: \" + jarContainerChoicePath,\n\t\t\t\t\t\t\tToast.LENGTH_LONG).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t\n\t\t\t// In this specific example executing this branch is correct since the target class is contained in a \n\t\t\t// repackaged version of the original class and so no class should be loaded!\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"SecureDexClassLoader found an invalid package as a container of the target class!\\nLeaving this activity..\",\n\t\t\t\t\t\t\tToast.LENGTH_LONG).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"SecureDexClassLoader discovers a repackaged container!\\nSo no dynamic loading operation will be allowed..\");\n\t\t\t\n\t\t}\n\t\t\n\t\treturn retComponentModifier;\n\t}\n\t\n\t/**\n\t * When one of the two initial buttons in this activity is clicked \n\t * a different component is dynamically loaded and used to customize\n\t * the rest of the layout.\n\t * \n\t * @param view\n\t */\n\tpublic void onBtnClick(View view) {\n\t\t\n\t\tif (isSecureModeChosen) {\n\t\t\t\n\t\t\tif (view.getId() == firstBtn.getId()) {\n\t\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifierSecurely(firstClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"First button was pressed..\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifierSecurely(secondClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"Second button was pressed..\");\n\t\t\t}\n\t\t\t\n\t\t} else {\n\t\t\n\t\t\tif (view.getId() == firstBtn.getId()) {\n\t\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifier(firstClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"First button was pressed..\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifier(secondClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"Second button was pressed..\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tList<Button> buttonList = new ArrayList<Button>();\n\t\tbuttonList.add(firstBtn);\n\t\tbuttonList.add(secondBtn);\n\t\tbuttonList.add(thirdBtn);\n\t\t\n\t\tif (mComponentModifier != null) {\n\t\t\t\n\t\t\t// The dynamic loaded class customizes all the components..\n\t\t\tmComponentModifier.customizeButtons(buttonList);\n\t\t\tmComponentModifier.customizeSwitch(switchSlider);\n\t\t\tmComponentModifier.customizeTextView(textView);\t\t\t\n\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"Customization process successfully completed.\");\n\t\t}\n\t\telse {\n\t\t\t\n\t\t\t// If no valid class was found just finish this activity..\n\t\t\tfinish();\n\t\t}\n\t\t\n\t}\n\t\n\t/**\n\t * This effect is used to end the activity.\n\t * \n\t * @param view\n\t */\n\tpublic void onBtnClickExit(View view) {\n\t\t\n\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\"Activity completed..\",\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\t\n\t\t});\n\t\t\n\t\tLog.d(TAG_DEX_SAMPLE, \"End of \" + R.string.title_activity_dex_class_sample + \" Activity.\");\n\t\t\n\t\tfinish();\n\t}\n\n}\n"
  },
  {
    "path": "example/ADT/src/it/polimi/poccodeloading/MainActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.polimi.poccodeloading;\n\nimport it.necst.grabnrun.SecureDexClassLoader;\nimport it.necst.grabnrun.SecureLoaderFactory;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport dalvik.system.DexClassLoader;\n\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Debug;\nimport android.os.Environment;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.Toast;\nimport android.widget.AdapterView.OnItemClickListener;\n\n/**\n * This activity is the entry point of the application.\n * By interacting with the different elements in the list of buttons\n * it is possible to trigger different ways to retrieve external code\n * from either a remote or a local path during the application execution.\n * \n * @author Luca Falsina\n *\n */\npublic class MainActivity extends Activity {\n\t\n\t// Variable used to activate profiling settings\n\tprivate static final boolean PROFILING_ON = false;\n\n\t// This array of strings contains the list of all the implemented\n\t// techniques for external code loading that should be visualized.\n\tprivate static final String techinquesToExecute[] = {\t\"DexClassLoader (.apk)\", \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SecureDexClassLoader (.apk)\", \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"DexClassLoader (.jar)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SecureDexClassLoader (.jar)\"};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//\"CreatePackageContext\"};\n\t\n\t// Auxiliary constants used for readability..\n\tprivate static final int DEX_CLASS_LOADER_APK = 0;\n\tprivate static final int SECURE_DEX_CLASS_LOADER_APK = 1;\n\tprivate static final int DEX_CLASS_LOADER_JAR = 2;\n\tprivate static final int SECURE_DEX_CLASS_LOADER_JAR = 3;\n\t// private static final int CREATE_PACK_CTX = 4;\n\t\n\t// Unique identifier used for Log entries\n\tprivate static final String TAG_MAIN = MainActivity.class.getSimpleName();\n\t\n\t// Extra passed to the intent to trigger the new activity with correct test parameters\n\tpublic static final String IS_SECURE_LOADING_CHOSEN = \"it.polimi.poccodeloading.IS_SECURE_LOADING_CHOSEN\";\n\t\n\t// Used to validate dynamic code loading operations..\n\tprivate boolean effectiveDexClassLoader, effectiveSecureDexClassLoader;\n\t\n\t// Strings which represent locations of the apk containers used for the test\n\t// and the name of the class to load dynamically..\n\tprivate String exampleTestAPKPath, exampleSignedAPKPath, exampleSignedChangedAPKPath, classNameInAPK;\n\t\n\t// Used to visualize helper toast messages..\n\tprivate Handler toastHandler;\n\t\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_main);\n\t\t\n\t\teffectiveDexClassLoader = false;\n\t\teffectiveSecureDexClassLoader = false;\n\t\t\n\t\ttoastHandler = new Handler();\n\t\t\n\t\t//String exampleTestAPKPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/NasaDailyImage.apk\";\n\t\t//String exampleTestAPKPath = Environment.getRootDirectory().getAbsolutePath() + \"/ext_card/download/NasaDailyImage/NasaDailyImage.apk\";\n\t\texampleTestAPKPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/NasaDailyImage/NasaDailyImageDebugSigned.apk\";\n\t\t\n\t\texampleSignedAPKPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/NasaDailyImage/NasaDailyImageSigned.apk\";\n\n\t\texampleSignedChangedAPKPath = \"https://dl.dropboxusercontent.com/u/28681922/NasaDailyImageSignedChangedDigest.apk\";\n\t\t\n\t\tclassNameInAPK = \"headfirstlab.nasadailyimage.NasaDailyImage\";\n\t\t\n\t\t// The list view element is retrieved..\n\t\tListView listView = (ListView) findViewById(R.id.listview);\n\t\t// Generate a dynamic list depending on the labels\n\t\tlistView.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, techinquesToExecute));\n\t\t\t\t\n\t\t// Create a message handling object as an anonymous class.\n\t\tOnItemClickListener mMessageClickedHandler = new OnItemClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n\t\t\t\t\t\t\n\t\t\t\t// Depending on the chosen button a different example case is taken..\n\t\t\t\tswitch(position) {\n\t\t\t\n\t\t\t\t\tcase DEX_CLASS_LOADER_APK:\n\t\t\t\t\t\teffectiveDexClassLoader = true;\n\t\t\t\t\t\tLog.d(TAG_MAIN, \"DexClassLoader from apk case should start.\");\n\t\t\t\t\t\tsetUpDexClassLoader();\n\t\t\t\t\t\teffectiveDexClassLoader = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\t\tcase SECURE_DEX_CLASS_LOADER_APK:\n\t\t\t\t\t\teffectiveSecureDexClassLoader = true;\n\t\t\t\t\t\tLog.d(TAG_MAIN, \"SecureDexClassLoader from apk case should start.\");\n\t\t\t\t\t\tif (PROFILING_ON) \n\t\t\t\t\t\t\tsetUpProfileSecureDexClassLoader();\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsetUpSecureDexClassLoader();\n\t\t\t\t\t\teffectiveSecureDexClassLoader = false;\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase DEX_CLASS_LOADER_JAR:\n\t\t\t\t\t\tIntent dexClassLoaderIntent = new Intent(MainActivity.this, DexClassSampleActivity.class);\n\t\t\t\t\t\tdexClassLoaderIntent.putExtra(IS_SECURE_LOADING_CHOSEN, false);\n\t\t\t\t\t\tLog.d(TAG_MAIN, \"DexClassLoader from jar case should start.\");\n\t\t\t\t\t\tstartActivity(dexClassLoaderIntent);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\t\tcase SECURE_DEX_CLASS_LOADER_JAR:\n\t\t\t\t\t\tIntent secureDexClassLoaderIntent = new Intent(MainActivity.this, DexClassSampleActivity.class);\n\t\t\t\t\t\tsecureDexClassLoaderIntent.putExtra(IS_SECURE_LOADING_CHOSEN, true);\n\t\t\t\t\t\tLog.d(TAG_MAIN, \"SecureDexClassLoader from jar case should start.\");\n\t\t\t\t\t\tstartActivity(secureDexClassLoaderIntent);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\n\t\t\t\t\t//case CREATE_PACK_CTX:\n\t\t\t\t\t\n\t\t\t\t\t\t//break;\n\t\t\t\t\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tLog.d(TAG_MAIN, \"Invalid button choice!\");\n\t\t\t\t}\n\t\t\t\n\t\t\t}\n\n\t\t};\n\n\t\tlistView.setOnItemClickListener(mMessageClickedHandler);\n\t\t\n\t}\n\n\tprotected void setUpProfileSecureDexClassLoader() {\n\t\t\n\t\t// First check: this operation can only start after \n\t\t// that the proper button has just been pressed..\n\t\tif (!effectiveSecureDexClassLoader) return;\n\t\t\t\t\t\t\n\t\tLog.d(TAG_MAIN, \"Setting up SecureDexClassLoader for profiling..\");\n\t\t// For the profiling test with SecureDexClassLoader I consider the worst performance scenario and so:\n\t\t// 1. The container is in a remote location and must be downloaded first\n\t\t// 2. The certificate, as well it's not cached but found at a remote URL and then imported\n\t\t// 3. The container in the end is correct so the full signature verification step is performed\n\t\t// 4. After the loading operation, the method to wipe out both the certificate and the container is invoked\n\t\t\n\t\tDebug.startMethodTracing(\"SecureDexClassLoader\");\n\t\t\n\t\tDebug.startMethodTracing(\"SecureDexFactory Preparation\");\n\t\t\n\t\t// Create an instance of SecureLoaderFactory..\n\t\t// It needs as a parameter a Context object (an Activity is an extension of such a class..)\n\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\t\t\n\t\tSecureDexClassLoader mSecureDexClassLoader;\n\t\t\n\t\t// Creating the apk paths list (only one path to a remote container in this case)\n\t\tString listAPKPaths = \"https://dl.dropboxusercontent.com/u/28681922/NasaDailyImageSigned.apk\";\n\t\t\n\t\t// Filling the associative map to link package name and certificate..\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\t\t// 1st Entry: valid REMOTE certificate location\n\t\ttry {\n\t\t\tpackageNamesToCertMap.put(\"headfirstlab.nasadailyimage\", new URL(\"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\"));\n\t\t} catch (MalformedURLException e) {\n\t\t\t// An invalid URL was provided for remote certificate location\n\t\t\tLog.e(TAG_MAIN, \"Invalid URL for remote certificate location!\");\n\t\t}\n\t\t\n\t\t// Instantiation of SecureDexClassLoader\n\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\tlistAPKPaths, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tClassLoader.getSystemClassLoader().getParent(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpackageNamesToCertMap);\n\t\t\n\t\tDebug.stopMethodTracing(); // end of \"SecureDexFactory Preparation\" section\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\t// Attempt to load dynamically the target class.. \n\t\t\tDebug.startMethodTracing(\"Load Operation\");\n\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\t\t\tDebug.stopMethodTracing(); // end of \"Load Operation\" section\n\t\t\t\n\t\t\t// Immediately wipe out all the cached data (certificate, container)\n\t\t\tDebug.startMethodTracing(\"Wipe Cached Data\");\n\t\t\tmSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\t\t\tDebug.stopMethodTracing(); // end of \"Wipe Cached Data\" section\n\t\t\t\n\t\t\tDebug.stopMethodTracing(); // end of \"SecureDexClassLoader\" section\n\t\t\t\n\t\t\tif (loadedClass != null) {\n\t\t\t\t\n\t\t\t\tfinal Activity NasaDailyActivity = (Activity) loadedClass.newInstance();\n\t\t\t\t\n\t\t\t\tLog.i(TAG_MAIN, \"Found valid class: \" + loadedClass.getSimpleName() + \"; APK path: \" + exampleSignedAPKPath.toString() + \"; Success!\");\n\t\t\t\t\n\t\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tToast.makeText(MainActivity.this,\n\t\t\t\t\t\t\t\t\"SecureDexClassLoader was successful! Found activity: \" + NasaDailyActivity.getClass().getName(),\n\t\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t});\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\tLog.w(TAG_MAIN, \"This time the chosen class should pass the security checks!\");\n\t\t\t}\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t\tLog.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n\t\t} catch (InstantiationException e) {\n\t\t\tLog.w(TAG_MAIN, \"Error while instanciating the loaded class!!\");\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\tLog.w(TAG_MAIN, \"Error while instanciating the loaded class!!\");\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\t\n\t}\n\n\tprotected void setUpSecureDexClassLoader() {\n\t\t\n\t\t// First check: this operation can only start after \n\t\t// that the proper button has just been pressed..\n\t\tif (!effectiveSecureDexClassLoader) return;\n\t\t\t\t\n\t\tLog.d(TAG_MAIN, \"Setting up SecureDexClassLoader..\");\n\t\t\n\t\t// Create an instance of SecureLoaderFactory..\n\t\t// It needs as a parameter a Context object (an Activity is an extension of such a class..)\n\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\t\t\n\t\tSecureDexClassLoader mSecureDexClassLoader;\n\t\t\n\t\t// Aim: Retrieve NasaDailyImage apk securely\n\t\t// 1st Test: Fetch the certificate by reverting package name --> FAIL\n\t\t// because no associative map was provided!! Even if you just want to revert package names\n\t\t// you must provide a map with entries like (\"any.package.name\", null) and then for each one of\n\t\t// those certificate location will be added by automatically reverting package names.\n\t\t\n\t\t// Creating the apk paths list (you can freely mix between remote and local URL)..\n\t\tString listAPKPaths = \tEnvironment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/testApp.apk:\" +\n\t\t\t\t\t\t\t\texampleTestAPKPath; \n\t\t\t\t\t\t\t\t// This last resource is downloaded from the web.\n\t\t\t\t\t\t\t\t//+ \":http://jdbc.postgresql.org/download/postgresql-9.2-1002.jdbc4.jar\";\n\t\t\n\t\tLog.i(TAG_MAIN, \"1st Test: Fetch the certificate by reverting package name with no associative map..\");\n\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(listAPKPaths, null, ClassLoader.getSystemClassLoader().getParent(), null);\t\t\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\t\t\t\n\t\t\tif (loadedClass != null) {\n\t\t\t\tLog.w(TAG_MAIN, \"No class should be returned in this case!!\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tLog.i(TAG_MAIN, \"SecureDexClassLoader loads nothing since no certificate should have been found. CORRECT!\");\n\t\t\t}\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\t\t\te.printStackTrace();\n\t\t\tLog.w(TAG_MAIN, \"No class should be searched in this case!!\");\n\t\t}\n\t\t\n\t\t// Remove the cached resources before the next test..\n\t\t// 2nd parameter may have been false as well since no certificate was fetched in this case..\n\t\tmSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\t\t\n\t\t// 2nd Test: Fetch the certificate by filling associative map \n\t\t// between package name and certificate --> FAIL cause the apk\n\t\t// was signed with the DEBUG ANDROID certificate\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\t// Filling the associative map to link package names and certificates..\n\t\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\t\t\t// 1st Entry: valid remote certificate location\n\t\t\tpackageNamesToCertMap.put(\"headfirstlab.nasadailyimage\", new URL(\"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\"));\n\t\t\t// 2nd Entry: inexistent certificate -> This link will be enforced to https but still there is no certificate at the final pointed URL\n\t\t\tpackageNamesToCertMap.put(\"it.polimi.example\", new URL(\"http://google.com/test_cert.pem\"));\n\t\t\t// 3rd Entry: misspelled and so invalid URL (missing a p..)\n\t\t\t// packageNamesToCertMap.put(\"it.polimi.example2\", \"htt://google.com/test_cert2.pem\");\n\t\t\t// 3rd Entry: reverse package name and then inexistent certificate at https://polimi.it/example3/certificate.pem\n\t\t\tpackageNamesToCertMap.put(\"it.polimi.example3\", null);\n\t\t\t\n\t\t\tLog.i(TAG_MAIN, \"2nd Test: Fetch the certificate by filling associative map..\");\n\t\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\texampleTestAPKPath, \n\t\t\t\t\tnull, \n\t\t\t\t\tClassLoader.getSystemClassLoader().getParent(),\n\t\t\t\t\tpackageNamesToCertMap);\n\t\t\t\n\t\t\ttry {\n\t\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\t\t\t\t\n\t\t\t\tif (loadedClass != null) {\n\t\t\t\t\t\n\t\t\t\t\tLog.w(TAG_MAIN, \"No class should be loaded!\");\n\t\t\t\t} else {\n\t\t\t\t\t\n\t\t\t\t\tLog.i(TAG_MAIN, \"This time the chosen class should find a certificate but it's the \" +\n\t\t\t\t\t\t\t\"wrong one! CORRECT!\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tLog.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n\t\t\t}\n\t\t\t\n\t\t\t// 3rd Test: Fetch the certificate by filling associative map \n\t\t\t// between package name and certificate --> FAIL cause some of \n\t\t\t// signatures in the container failed the verification process\n\t\t\t// against the developer certificate.\n\t\t\tLog.i(TAG_MAIN, \"3rd Test: Fetch the certificate by filling associative map..\");\n\t\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\texampleSignedChangedAPKPath, \n\t\t\t\t\tnull, \n\t\t\t\t\tClassLoader.getSystemClassLoader().getParent(),\n\t\t\t\t\tpackageNamesToCertMap); \n\t\t\t\n\t\t\ttry {\n\t\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\t\t\t\t\n\t\t\t\tif (loadedClass != null) {\n\t\t\t\t\t\n\t\t\t\t\tLog.w(TAG_MAIN, \"No class should be loaded!\");\n\t\t\t\t} else {\n\t\t\t\t\t\n\t\t\t\t\tLog.i(TAG_MAIN, \"This time the chosen class should find a certificate but the \" +\n\t\t\t\t\t\t\t\"apk container signatures do not match all properly and so no class loading! CORRECT!\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tLog.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n\t\t\t}\n\t\t\t\n\t\t\t// 4th Test: Fetch the certificate by filling associative map \n\t\t\t// between package name and certificate --> SUCCESS cause this \n\t\t\t// time the apk was signed with the correct certificate\n\t\t\tLog.i(TAG_MAIN, \"4th Test: Fetch the certificate by filling associative map..\");\n\t\t\t\n\t\t\t// Creating the apk paths list (you can mix between remote and local URL)..\n\t\t\tlistAPKPaths = \t\"http://google.com/testApp2.apk:\" + exampleSignedAPKPath;\n\t\t\t\n\t\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\tlistAPKPaths, \n\t\t\t\t\tnull, \n\t\t\t\t\tClassLoader.getSystemClassLoader().getParent(),\n\t\t\t\t\tpackageNamesToCertMap);\n\t\t\t\n\t\t\ttry {\n\t\t\t\tClass<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\t\t\t\t\n\t\t\t\tif (loadedClass != null) {\n\t\t\t\t\t\n\t\t\t\t\tfinal Activity NasaDailyActivity = (Activity) loadedClass.newInstance();\n\t\t\t\t\t\n\t\t\t\t\tLog.i(TAG_MAIN, \"Found valid class: \" + loadedClass.getSimpleName() + \"; APK path: \" + exampleSignedAPKPath.toString() + \"; Success!\");\n\t\t\t\t\t\n\t\t\t\t\ttoastHandler.post(new Runnable() {\n\t\t\t\t\t\t\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tToast.makeText(MainActivity.this,\n\t\t\t\t\t\t\t\t\t\"SecureDexClassLoader was successful! Found activity: \" + NasaDailyActivity.getClass().getName(),\n\t\t\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t});\n\t\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\t\n\t\t\t\t\tLog.w(TAG_MAIN, \"This time the chosen class should pass the security checks!\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t\tLog.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n\t\t\t} catch (InstantiationException e) {\n\t\t\t\tLog.w(TAG_MAIN, \"Error while instanciating the loaded class!!\");\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (IllegalAccessException e) {\n\t\t\t\tLog.w(TAG_MAIN, \"Error while instanciating the loaded class!!\");\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\t\n\t\t\t// Remove the cached certificates and the certificate..\n\t\t\tmSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\t\t\tLog.d(TAG_MAIN, \"Cached data of SecureDexClassLoader have been wiped out..\");\n\t\t\t\n\t\t} catch (MalformedURLException e) {\n\t\t\t\n\t\t\t// The previous entries for the map are not necessarily the right ones \n\t\t\t// but still they are not malformed so no exception should be raised.\n\t\t\tLog.e(TAG_MAIN, \"A malformed URL was provided for a remote certificate location\");\n\t\t\t\n\t\t}\n\n\t\t\n\t}\n\n\t/**\n\t * This method is used to set up and manage a DexClassLoader component in \n\t * order to retrieve a new activity from an .apk, which has been \n\t * already downloaded and installed on the mobile device.\n\t * If everything works fine, it will instantiate the main activity of \n\t * this .apk.\n\t * \n\t */\n\tprotected void setUpDexClassLoader() {\n\t\t\n\t\t// First check: this operation can only start after \n\t\t// that the proper button has just been pressed..\n\t\tif (!effectiveDexClassLoader) return;\n\t\t\n\t\tLog.d(TAG_MAIN, \"Setting up DexClassLoader..\");\n\t\t\n\t\tif (PROFILING_ON) Debug.startMethodTracing(\"DexClassLoader\");\n\t\t\n\t\tFile dexOutputDir = getDir(\"dex\", MODE_PRIVATE);\n\t\tDexClassLoader mDexClassLoader = new DexClassLoader(\texampleTestAPKPath, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdexOutputDir.getAbsolutePath(), \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tClassLoader.getSystemClassLoader().getParent());\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\t// Load NasaDailyImage Main Activity..\n\t\t\tClass<?> loadedClass = mDexClassLoader.loadClass(classNameInAPK);\n\t\t\t\n\t\t\tif (PROFILING_ON) Debug.stopMethodTracing(); // end of \"DexClassLoader\" trace\n\t\t\t\n\t\t\tfinal Activity NasaDailyActivity = (Activity) loadedClass.newInstance();\n\t\t\t\n\t\t\t// Note that in this case loading class operation was performed even if the APK which contains\n\t\t\t// the target class was signed just with the Android Debug key. This operation would have failed\n\t\t\t// if SecureDexClassLoader would have been used in stead..\n\t\t\tLog.i(TAG_MAIN, \"Found class: \" + loadedClass.getSimpleName() + \"; APK path: \" + exampleTestAPKPath.toString());\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(MainActivity.this,\n\t\t\t\t\t\t\t\"DexClassLoader was successful! Found activity: \" + NasaDailyActivity.getClass().getName(),\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\t// An intent is defined to start the new loaded activity.\n\t\t\t//Intent transitionIntent = new Intent(this, loadedClass);\n\t\t\t//startActivity(transitionIntent);\n\t\t\t//transitionIntent.setClassName(\"headfirstlab.nasadailyimage\", \"headfirstlab.nasadailyimage.NasaDailyImage\");\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\n\t\t\tLog.e(TAG_MAIN, \"Error: Class not found!\");\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(MainActivity.this,\n\t\t\t\t\t\t\t\"Error! No class found for DexClassLoader..\",\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\te.printStackTrace();\n\t\t} catch (ActivityNotFoundException e) {\n\t\t\n\t\t\tLog.e(TAG_MAIN, \"Error: Activity not found in the manifest!\");\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(MainActivity.this,\n\t\t\t\t\t\t\t\"Error! The activity found by DexClassLoader is not a legitimate one..\",\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "example/AS/.gitignore",
    "content": ".gradle/\napp/build/\nbuild/\nimport-summary.txt\nlocal.properties\n\n## Directory-based project format:\n.idea/\n\n## IntelliJ config files\n*.iml\n\n"
  },
  {
    "path": "example/AS/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"it.polimi.poccodeloading\"\n        minSdkVersion 16\n        targetSdkVersion 21\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n\ndependencies {\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'it.necst.grabnrun:grabnrun:1.0.4'\n}\n"
  },
  {
    "path": "example/AS/app/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "example/AS/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"it.polimi.poccodeloading\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"16\"\n        android:targetSdkVersion=\"21\" />\n    \n    <uses-permission android:name=\"android.permission.INTERNET\"></uses-permission>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"></uses-permission>\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"></uses-permission>\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\" >\n        <activity\n            android:name=\"it.polimi.poccodeloading.MainActivity\"\n            android:screenOrientation=\"landscape\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize\"\n            android:label=\"@string/app_name\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"it.polimi.poccodeloading.DexClassSampleActivity\"\n            android:label=\"@string/title_activity_dex_class_sample\" \n            android:parentActivityName=\"it.polimi.poccodeloading.MainActivity\"\n            android:screenOrientation=\"landscape\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize\" >\n        </activity>\n        <activity\n            android:name=\"headfirstlab.nasadailyimage.NasaDailyImage\"\n            android:label=\"@string/title_activity_loaded_dynamically\"\n            android:parentActivityName=\"it.polimi.poccodeloading.MainActivity\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize\" >\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "example/AS/app/src/main/java/it/polimi/poccodeloading/ComponentModifier.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.polimi.poccodeloading;\n\nimport java.util.List;\n\nimport android.widget.Button;\nimport android.widget.Switch;\nimport android.widget.TextView;\n\n/**\n * Interface defined to mask the dynamic retrieval of the customizer\n * used to modify the layout of GUI components.\n * \n * @author Luca Falsina\n */\npublic interface ComponentModifier {\n\t\n\tvoid customizeButtons(List<Button> buttonList);\n\t\n\tvoid customizeSwitch(Switch switchSlider);\n\n\tvoid customizeTextView(TextView textView);\n\n}\n"
  },
  {
    "path": "example/AS/app/src/main/java/it/polimi/poccodeloading/DexClassSampleActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.polimi.poccodeloading;\n\nimport it.necst.grabnrun.SecureDexClassLoader;\nimport it.necst.grabnrun.SecureLoaderFactory;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\n\nimport dalvik.system.DexClassLoader;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.CompoundButton;\nimport android.widget.CompoundButton.OnCheckedChangeListener;\nimport android.widget.Switch;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\n/**\n * This activity shows a small example in which a customizer element is loaded\n * dynamically at run time through a DexClassLoader API call.\n * Depending on the provided jar container this object will customize in a \n * different way the layout of the components in the GUI even if the same \n * method calls are executed.\n * \n * @author Luca Falsina\n */\npublic class DexClassSampleActivity extends Activity {\n\n\t// Unique identifier used for Log entries\n\tprivate static final String TAG_DEX_SAMPLE = DexClassSampleActivity.class.getSimpleName();\n\t\n\tprivate boolean isSecureModeChosen;\n\t\n\t// This is the interface used to mask the object\n\t// retrieved from the external jar..\n\tprivate ComponentModifier mComponentModifier;\n\t\n\t//private String assetSuffix = \"/exampleJar/componentModifier.jar\";\n\t\n\t// Used to pick different classes dynamically at run time\n\tprivate final String firstClassName = \"it.polimi.componentmodifier.FirstComponentModifierImpl\";\n\tprivate final String secondClassName = \"it.polimi.componentmodifier.SecondComponentModifierImpl\";\n\t\n\t// Used to visualize helper toast messages..\n\tprivate Handler toastHandler;\n\t\n\t// Components of the layout\n\tprivate TextView textView;\n\tprivate Button firstBtn, secondBtn, thirdBtn;\n\tprivate Switch switchSlider;\n\t\n\t// Path where \"componentModifier.jar\" and its repackaged version are stored on the device.\n\tprivate String jarContainerPath, jarContainerRepackPath;\n\t\n\t// Initialized only if the secure mode is enabled..\n\tprivate SecureDexClassLoader mSecureDexClassLoader;\n\t\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_dex_class_sample);\n\t\tsetTitle(getString(R.string.title_activity_dex_class_sample));\n\t\t\n\t\ttoastHandler = new Handler();\n\t\t\n\t\t// Retrieve the intent of the launching activity\n\t\tIntent intent = getIntent();\n\t\t\n\t\t// Enable/Disable secure loading;\n\t\tisSecureModeChosen = intent.getBooleanExtra(MainActivity.IS_SECURE_LOADING_CHOSEN, false);\n\t\t\n\t\tmSecureDexClassLoader = null;\n\t\t\n\t\t// final String jarContainerPath = getAssets() + assetSuffix;\n\t\tjarContainerPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/componentModifier.jar\";\n\t\t// final String jarContainerPath = \"https://github.com/lukeFalsina/test/blob/master/componentModifier.jar\";\n\t\t//jarContainerRepackPath = \"https://dl.dropboxusercontent.com/u/28681922/componentModifierRepack.jar\";\n\t\tjarContainerRepackPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/componentModifierRepack.jar\";\n\n\t\t// Retrieve all the components, which are going to be modified\n\t\t// by the instance of ComponentModifier\n\t\ttextView = (TextView) findViewById(R.id.exp_text_dex_load_jar);\n\t\tfirstBtn = (Button) findViewById(R.id.button1_dex_load_jar);\n\t\tsecondBtn = (Button) findViewById(R.id.button2_dex_load_jar);\n\t\tthirdBtn = (Button) findViewById(R.id.button3_dex_load_jar);\n\t\tswitchSlider = (Switch) findViewById(R.id.switch_dex_load_jar);\n\t\tswitchSlider.setOnCheckedChangeListener(new OnCheckedChangeListener() {\n\n\t\t\t   @Override\n\t\t\t   public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {\n\n\t\t\t\t   if(isChecked){\n\t\t\t\t\t   onBtnClickExit(buttonView);\n\t\t\t\t   }\n\t\t\t   }\n\t\t});\n\t\t\n\t}\n\t\n\t// A simple method that randomly assign the path container of either the\n\t// original and benign jar container or the repackaged one.\n\tprivate String randomContainerPathChoice() {\n\t\t\n\t\tRandom randomBooleanGen = new Random();\n\t\tboolean randomBoolean = randomBooleanGen.nextBoolean();\n\t\t\n\t\tif (!randomBoolean) return jarContainerPath;\n\t\telse return jarContainerRepackPath;\n\t}\n\t\n\tprivate ComponentModifier retrieveComponentModifier(String className) {\n\n\t\tLog.d(TAG_DEX_SAMPLE, \"Setting up Dex Class Loader..\");\n\t\t\n\t\tComponentModifier retComponentModifier = null;\n\t\t\n\t\tfinal String jarContainerChoicePath = randomContainerPathChoice();\n\t\t\n\t\tFile dexOutputDir = getDir(\"dex\", MODE_PRIVATE);\n\t\t\n\t\tDexClassLoader mDexClassLoader = new DexClassLoader(\n\t\t\t\tjarContainerChoicePath,\n\t\t\t\tdexOutputDir.getAbsolutePath(),\n\t\t\t\tnull,\n\t\t\t\tgetClass().getClassLoader());\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\t// It does not matter which one of the two containers is selected..\n\t\t\t// DexClassLoader will always return a class from either the benign or the\n\t\t\t// repackaged container..\n\t\t\tClass<?> loadedClass = mDexClassLoader.loadClass(className);\n\t\t\t\n\t\t\tretComponentModifier = (ComponentModifier) loadedClass.newInstance();\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Class not found!\");\n\t\t\te.printStackTrace();\n\t\t} catch (InstantiationException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Instantiation issues!\");\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalAccessException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Illegal access!\");\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\tif (retComponentModifier != null) {\n\t\t\t\n\t\t\tfinal String shortClassName = retComponentModifier.getClass().getSimpleName();\n\t\t\t\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"DexClassLoader was successful!\\nLoaded class name:\" + shortClassName + \"\\nPath: \" + jarContainerChoicePath);\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"DexClassLoader was successful!\\nLoaded class name: \" + shortClassName + \"\\nPath: \" + jarContainerChoicePath,\n\t\t\t\t\t\t\tToast.LENGTH_LONG).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"DexClassLoader failed!\\nLeaving this activity..\",\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\t// Exit this activity..\n\t\t\tfinish();\n\t\t}\n\t\t\n\t\treturn retComponentModifier;\n\t}\n\t\n\tprivate ComponentModifier retrieveComponentModifierSecurely(String className) {\n\t\t\n\t\tLog.d(TAG_DEX_SAMPLE, \"Setting up SecureDexClassLoader..\");\n\t\t\n\t\tComponentModifier retComponentModifier = null;\n\t\t\n\t\tfinal String jarContainerChoicePath = randomContainerPathChoice();\n\t\t\n\t\tSecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\t\t\n\t\t// Filling the associative map to link package names and certificates..\n\t\tMap<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n\t\t// 1st Entry: valid remote certificate location\n\t\ttry {\n\t\t\tpackageNamesToCertMap.put(\"it.polimi.componentmodifier\", new URL(\"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\"));\n\t\t} catch (MalformedURLException e1) {\n\t\t\t// A malformed URL was used for remote certificate location\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"A malformed URL was used for remote certificate location!\");\n\t\t}\n\t\t\n\t\t// Initialize SecureDexClassLoader with repackaged jar container..\n\t\tmSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\tjarContainerChoicePath, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull, \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tgetClass().getClassLoader(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpackageNamesToCertMap);\n\t\t\n\t\t// Class returned from the loadClass() operation..\n\t\tClass<?> loadedClass = null;\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\tloadedClass = mSecureDexClassLoader.loadClass(className);\n\t\t\t\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tLog.e(TAG_DEX_SAMPLE, \"Error: Class not found!\");\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\tif (loadedClass != null) {\n\t\t\t\n\t\t\t// In this scenario the provided container is a repackaged version of the benign one\n\t\t\t// so if this branch is accessed, it means that SecureDexClassLoader failed in\n\t\t\t// the signature verification step in loadClass() method.\n\t\t\ttry {\n\t\t\t\tretComponentModifier = (ComponentModifier) loadedClass.newInstance();\n\t\t\t} catch (InstantiationException e) {\n\t\t\t\tLog.e(TAG_DEX_SAMPLE, \"Problem in the instantiation of the target class!\");\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (IllegalAccessException e) {\n\t\t\t\tLog.e(TAG_DEX_SAMPLE, \"Problem in the access to the target class!\");\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\t\n\t\t\tfinal String shortClassName = retComponentModifier.getClass().getSimpleName();\n\t\t\t\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"SecureDexClassLoader found out a consistent container!\\nLoaded class name:\" + shortClassName + \"\\nPath: \" + jarContainerChoicePath);\n\t\t\t\n\t\t\t// Erase all the cached resources..\n\t\t\t// mSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\t\t\t\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"SecureDexClassLoader found out a valid ComponentModifier container!\\nLoaded class name: \" + shortClassName + \"\\nPath: \" + jarContainerChoicePath,\n\t\t\t\t\t\t\tToast.LENGTH_LONG).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\t\n\t\t\t// In this specific example executing this branch is correct since the target class is contained in a \n\t\t\t// repackaged version of the original class and so no class should be loaded!\n\t\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\t\"SecureDexClassLoader found an invalid package as a container of the target class!\\nLeaving this activity..\",\n\t\t\t\t\t\t\tToast.LENGTH_LONG).show();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t});\n\t\t\t\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"SecureDexClassLoader discovers a repackaged container!\\nSo no dynamic loading operation will be allowed..\");\n\t\t\t\n\t\t}\n\t\t\n\t\treturn retComponentModifier;\n\t}\n\t\n\t/**\n\t * When one of the two initial buttons in this activity is clicked \n\t * a different component is dynamically loaded and used to customize\n\t * the rest of the layout.\n\t * \n\t * @param view\n\t */\n\tpublic void onBtnClick(View view) {\n\t\t\n\t\tif (isSecureModeChosen) {\n\t\t\t\n\t\t\tif (view.getId() == firstBtn.getId()) {\n\t\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifierSecurely(firstClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"First button was pressed..\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifierSecurely(secondClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"Second button was pressed..\");\n\t\t\t}\n\t\t\t\n\t\t} else {\n\t\t\n\t\t\tif (view.getId() == firstBtn.getId()) {\n\t\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifier(firstClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"First button was pressed..\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\n\t\t\t\tmComponentModifier = retrieveComponentModifier(secondClassName);\n\t\t\t\tLog.d(TAG_DEX_SAMPLE, \"Second button was pressed..\");\n\t\t\t}\n\t\t}\n\t\t\n\t\tList<Button> buttonList = new ArrayList<Button>();\n\t\tbuttonList.add(firstBtn);\n\t\tbuttonList.add(secondBtn);\n\t\tbuttonList.add(thirdBtn);\n\t\t\n\t\tif (mComponentModifier != null) {\n\t\t\t\n\t\t\t// The dynamic loaded class customizes all the components..\n\t\t\tmComponentModifier.customizeButtons(buttonList);\n\t\t\tmComponentModifier.customizeSwitch(switchSlider);\n\t\t\tmComponentModifier.customizeTextView(textView);\t\t\t\n\n\t\t\tLog.i(TAG_DEX_SAMPLE, \"Customization process successfully completed.\");\n\t\t}\n\t\telse {\n\t\t\t\n\t\t\t// If no valid class was found just finish this activity..\n\t\t\tfinish();\n\t\t}\n\t\t\n\t}\n\t\n\t/**\n\t * This effect is used to end the activity.\n\t * \n\t * @param view\n\t */\n\tpublic void onBtnClickExit(View view) {\n\t\t\n\t\ttoastHandler.post(new Runnable() {\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tToast.makeText(DexClassSampleActivity.this,\n\t\t\t\t\t\t\"Activity completed..\",\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t\t\n\t\t});\n\t\t\n\t\tLog.d(TAG_DEX_SAMPLE, \"End of \" + R.string.title_activity_dex_class_sample + \" Activity.\");\n\t\t\n\t\tfinish();\n\t}\n\n}\n"
  },
  {
    "path": "example/AS/app/src/main/java/it/polimi/poccodeloading/MainActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.polimi.poccodeloading;\n\nimport static android.os.Environment.getExternalStorageDirectory;\n\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Debug;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.AdapterView.OnItemClickListener;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport dalvik.system.DexClassLoader;\nimport it.necst.grabnrun.SecureDexClassLoader;\nimport it.necst.grabnrun.SecureLoaderFactory;\n\n/**\n * This activity is the entry point of the application.\n * By interacting with the different elements in the list of buttons\n * it is possible to trigger different ways to retrieve external code\n * from either a remote or a local path during the application execution.\n *\n * @author Luca Falsina\n */\npublic class MainActivity extends Activity {\n\n    // Extra passed to the intent to trigger the new activity with correct test parameters\n    public static final String IS_SECURE_LOADING_CHOSEN = \"it.polimi.poccodeloading.IS_SECURE_LOADING_CHOSEN\";\n    // Variable used to activate profiling settings\n    private static final boolean PROFILING_ON = false;\n    // This array of strings contains the list of all the implemented\n    // techniques for external code loading that should be visualized.\n    private static final String techniquesToExecute[] = {\t\"DexClassLoader (.apk)\",\n            \"SecureDexClassLoader (.apk)\",\n            \"DexClassLoader (.jar)\",\n            \"SecureDexClassLoader (.jar)\"};\n    // Auxiliary constants used for readability..\n    private static final int DEX_CLASS_LOADER_APK = 0;\n    private static final int SECURE_DEX_CLASS_LOADER_APK = 1;\n    private static final int DEX_CLASS_LOADER_JAR = 2;\n    private static final int SECURE_DEX_CLASS_LOADER_JAR = 3;\n    // Unique identifier used for Log entries\n    private static final String TAG_MAIN = MainActivity.class.getSimpleName();\n    // Used to validate dynamic code loading operations..\n    private boolean effectiveDexClassLoader, effectiveSecureDexClassLoader;\n\n    // Strings which represent locations of the apk containers used for the test\n    // and the name of the class to load dynamically..\n    private String exampleTestAPKPath, exampleSignedAPKPath, exampleSignedChangedAPKPath, classNameInAPK;\n\n    // Used to visualize helper toast messages..\n    private Handler toastHandler;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        effectiveDexClassLoader = false;\n        effectiveSecureDexClassLoader = false;\n\n        toastHandler = new Handler();\n\n        //String exampleTestAPKPath = Environment.getExternalStorageDirectory().getAbsolutePath() + \"/Download/NasaDailyImage.apk\";\n        //String exampleTestAPKPath = Environment.getRootDirectory().getAbsolutePath() + \"/ext_card/download/NasaDailyImage/NasaDailyImage.apk\";\n        exampleTestAPKPath = getExternalStorageDirectory().getAbsolutePath() + \"/Download/NasaDailyImage/NasaDailyImageDebugSigned.apk\";\n\n        exampleSignedAPKPath = getExternalStorageDirectory().getAbsolutePath() + \"/Download/NasaDailyImage/NasaDailyImageSigned.apk\";\n\n        exampleSignedChangedAPKPath = \"https://dl.dropboxusercontent.com/u/28681922/NasaDailyImageSignedChangedDigest.apk\";\n\n        classNameInAPK = \"headfirstlab.nasadailyimage.NasaDailyImage\";\n\n        // The list view element is retrieved..\n        ListView listView = (ListView) findViewById(R.id.listview);\n        // Generate a dynamic list depending on the labels\n        listView.setAdapter(new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, techniquesToExecute));\n\n        // Create a message handling object as an anonymous class.\n        OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {\n\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n\n                // Depending on the chosen button a different example case is taken..\n                switch(position) {\n\n                    case DEX_CLASS_LOADER_APK:\n                        effectiveDexClassLoader = true;\n                        Log.d(TAG_MAIN, \"DexClassLoader from apk case should start.\");\n                        setUpDexClassLoader();\n                        effectiveDexClassLoader = false;\n                        break;\n\n                    case SECURE_DEX_CLASS_LOADER_APK:\n                        effectiveSecureDexClassLoader = true;\n                        Log.d(TAG_MAIN, \"SecureDexClassLoader from apk case should start.\");\n                        if (PROFILING_ON)\n                            setUpProfileSecureDexClassLoader();\n                        else\n                            setUpSecureDexClassLoader();\n                        effectiveSecureDexClassLoader = false;\n                        break;\n\n                    case DEX_CLASS_LOADER_JAR:\n                        Intent dexClassLoaderIntent = new Intent(MainActivity.this, DexClassSampleActivity.class);\n                        dexClassLoaderIntent.putExtra(IS_SECURE_LOADING_CHOSEN, false);\n                        Log.d(TAG_MAIN, \"DexClassLoader from jar case should start.\");\n                        startActivity(dexClassLoaderIntent);\n                        break;\n\n                    case SECURE_DEX_CLASS_LOADER_JAR:\n                        Intent secureDexClassLoaderIntent = new Intent(MainActivity.this, DexClassSampleActivity.class);\n                        secureDexClassLoaderIntent.putExtra(IS_SECURE_LOADING_CHOSEN, true);\n                        Log.d(TAG_MAIN, \"SecureDexClassLoader from jar case should start.\");\n                        startActivity(secureDexClassLoaderIntent);\n                        break;\n\n                    default:\n                        Log.d(TAG_MAIN, \"Invalid button choice!\");\n                }\n            }\n        };\n\n        listView.setOnItemClickListener(mMessageClickedHandler);\n    }\n\n    protected void setUpProfileSecureDexClassLoader() {\n\n        // First check: this operation can only start after\n        // that the proper button has just been pressed..\n        if (!effectiveSecureDexClassLoader) return;\n\n        Log.d(TAG_MAIN, \"Setting up SecureDexClassLoader for profiling..\");\n        // For the profiling test with SecureDexClassLoader I consider the worst performance scenario and so:\n        // 1. The container is in a remote location and must be downloaded first\n        // 2. The certificate, as well it's not cached but found at a remote URL and then imported\n        // 3. The container in the end is correct so the full signature verification step is performed\n        // 4. After the loading operation, the method to wipe out both the certificate and the container is invoked\n\n        Debug.startMethodTracing(\"SecureDexClassLoader\");\n\n        Debug.startMethodTracing(\"SecureDexFactory Preparation\");\n\n        // Create an instance of SecureLoaderFactory..\n        // It needs as a parameter a Context object (an Activity is an extension of such a class..)\n        SecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\n        SecureDexClassLoader mSecureDexClassLoader;\n\n        // Creating the apk paths list (only one path to a remote container in this case)\n        String listAPKPaths = \"https://dl.dropboxusercontent.com/u/28681922/NasaDailyImageSigned.apk\";\n\n        // Filling the associative map to link package name and certificate..\n        Map<String, URL> packageNamesToCertMap = new HashMap<String, URL>();\n        // 1st Entry: valid REMOTE certificate location\n        try {\n            packageNamesToCertMap.put(\"headfirstlab.nasadailyimage\", new URL(\"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\"));\n        } catch (MalformedURLException e) {\n            // An invalid URL was provided for remote certificate location\n            Log.e(TAG_MAIN, \"Invalid URL for remote certificate location!\");\n        }\n\n        // Instantiation of SecureDexClassLoader\n        mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\tlistAPKPaths,\n                null,\n                ClassLoader.getSystemClassLoader().getParent(),\n                packageNamesToCertMap);\n\n        Debug.stopMethodTracing(); // end of \"SecureDexFactory Preparation\" section\n\n        try {\n\n            // Attempt to load dynamically the target class..\n            Debug.startMethodTracing(\"Load Operation\");\n            Class<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n            Debug.stopMethodTracing(); // end of \"Load Operation\" section\n\n            // Immediately wipe out all the cached data (certificate, container)\n            Debug.startMethodTracing(\"Wipe Cached Data\");\n            mSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n            Debug.stopMethodTracing(); // end of \"Wipe Cached Data\" section\n\n            Debug.stopMethodTracing(); // end of \"SecureDexClassLoader\" section\n\n            if (loadedClass != null) {\n\n                final Activity NasaDailyActivity = (Activity) loadedClass.newInstance();\n\n                Log.i(TAG_MAIN, \"Found valid class: \" + loadedClass.getSimpleName() + \"; APK path: \" + exampleSignedAPKPath + \"; Success!\");\n\n                toastHandler.post(new Runnable() {\n\n                    @Override\n                    public void run() {\n                        Toast.makeText(MainActivity.this,\n                                \"SecureDexClassLoader was successful! Found activity: \" + NasaDailyActivity.getClass().getName(),\n                                Toast.LENGTH_SHORT).show();\n                    }\n\n                });\n\n            } else {\n\n                Log.w(TAG_MAIN, \"This time the chosen class should pass the security checks!\");\n            }\n\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n            Log.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n        } catch (InstantiationException e) {\n            Log.w(TAG_MAIN, \"Error while instantiating the loaded class!!\");\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            Log.w(TAG_MAIN, \"Error while instantiating the loaded class!!\");\n            e.printStackTrace();\n        }\n    }\n\n    protected void setUpSecureDexClassLoader() {\n\n        // First check: this operation can only start after\n        // that the proper button has just been pressed..\n        if (!effectiveSecureDexClassLoader) return;\n\n        Log.d(TAG_MAIN, \"Setting up SecureDexClassLoader..\");\n\n        // Create an instance of SecureLoaderFactory..\n        // It needs as a parameter a Context object (an Activity is an extension of such a class..)\n        SecureLoaderFactory mSecureLoaderFactory = new SecureLoaderFactory(this);\n\n        SecureDexClassLoader mSecureDexClassLoader;\n\n        // Aim: Retrieve NasaDailyImage apk securely\n\n        // 1st Test: Provide a null associative map as a fourth parameter when\n        // creating a SecureDexClassLoader. This test case was removed since the\n        // latest versions of GNR (>= 1.0.3) will raise a NullPointerException.\n        // For more details on this test case, see:\n        // http://grab-n-run.readthedocs.org/en/latest/example.html#setupsecuredexclassloader\n\n        // 2nd Test: Fetch the certificate by filling associative map\n        // between package name and certificate --> FAIL cause the apk\n        // was signed with the DEBUG ANDROID private key, and not with the trusted one.\n\n        try {\n\n            // Filling the associative map to link package names and certificates..\n            Map<String, URL> packageNamesToCertMap = new HashMap<>();\n            // 1st Entry: valid remote certificate location\n            packageNamesToCertMap.put(\"headfirstlab.nasadailyimage\", new URL(\"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\"));\n            // 2nd Entry: not existent certificate -> This link will be enforced to https,\n            // but still there is no certificate at the secure endpoint\n            packageNamesToCertMap.put(\"it.polimi.example\", new URL(\"http://google.com/test_cert.pem\"));\n            // 3rd Entry: misspelled and so invalid URL (missing a p..)\n            // packageNamesToCertMap.put(\"it.polimi.example2\", \"htt://google.com/test_cert2.pem\");\n            // 3rd Entry: reverse package name and then inexistent certificate at https://polimi.it/example3/certificate.pem\n            packageNamesToCertMap.put(\"it.polimi.example3\", null);\n\n            Log.i(TAG_MAIN, \"2nd Test: Evaluate container signed with the Android debug private key.\");\n            mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\n                    exampleTestAPKPath,\n                    null,\n                    ClassLoader.getSystemClassLoader(),\n                    packageNamesToCertMap);\n\n            try {\n                Class<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\n                if (loadedClass != null) {\n\n                    Log.w(TAG_MAIN, \"No class should be loaded!\");\n                } else {\n\n                    Log.i(TAG_MAIN, \"The chosen class is signed but it does not pass \" +\n                            \"the verification against the trusted certificate! CORRECT!\");\n                }\n\n            } catch (ClassNotFoundException e) {\n                e.printStackTrace();\n                Log.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n            }\n\n            // 3rd Test: Fetch the certificate by filling associative map\n            // between package name and certificate --> FAIL cause some of\n            // signatures in the container failed the verification process\n            // against the trusted developer certificate.\n            Log.i(TAG_MAIN, \"3rd Test: Evaluate container with some tampered entries..\");\n            mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\n                    exampleSignedChangedAPKPath,\n                    null,\n                    ClassLoader.getSystemClassLoader(),\n                    packageNamesToCertMap);\n\n            try {\n                Class<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\n                if (loadedClass != null) {\n\n                    Log.w(TAG_MAIN, \"No class should be loaded!\");\n                } else {\n\n                    Log.i(TAG_MAIN, \"The chosen class was signed but the \" +\n                            \"apk container was tampered, thus not all the \" +\n                            \"signatures match anymore. Therefore so no class loading! CORRECT!\");\n                }\n\n            } catch (ClassNotFoundException e) {\n                e.printStackTrace();\n                Log.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n            }\n\n            // 4th Test: Fetch the certificate by filling associative map\n            // between package name and certificate --> SUCCESS cause this\n            // time the apk was signed and successfully verified against the correct certificate\n            Log.i(TAG_MAIN, \"4th Test: Fetch the certificate by filling associative map..\");\n\n            // Creating the apk paths list (you can mix between remote and local URL)..\n            String listAPKPaths =\n                    \"http://google.com/testApp2.apk:\" + exampleSignedAPKPath;\n\n            mSecureDexClassLoader = mSecureLoaderFactory.createDexClassLoader(\n                    listAPKPaths,\n                    null,\n                    ClassLoader.getSystemClassLoader(),\n                    packageNamesToCertMap);\n\n            try {\n                Class<?> loadedClass = mSecureDexClassLoader.loadClass(classNameInAPK);\n\n                if (loadedClass != null) {\n\n                    final Activity NasaDailyActivity = (Activity) loadedClass.newInstance();\n\n                    Log.i(TAG_MAIN, \"Found valid class: \" + loadedClass.getSimpleName() + \"; APK path: \" + exampleSignedAPKPath + \"; Success!\");\n\n                    toastHandler.post(new Runnable() {\n\n                        @Override\n                        public void run() {\n                            Toast.makeText(MainActivity.this,\n                                    \"SecureDexClassLoader was successful! Found activity: \" + NasaDailyActivity.getClass().getName(),\n                                    Toast.LENGTH_SHORT).show();\n                        }\n\n                    });\n\n                } else {\n\n                    Log.w(TAG_MAIN, \"This time the chosen class should pass the security verification!\");\n                }\n\n            } catch (ClassNotFoundException e) {\n                e.printStackTrace();\n                Log.w(TAG_MAIN, \"Class should be present in the provided path!!\");\n            } catch (InstantiationException e) {\n                Log.w(TAG_MAIN, \"Error while instantiating the loaded class!!\");\n                e.printStackTrace();\n            } catch (IllegalAccessException e) {\n                Log.w(TAG_MAIN, \"Error while instantiating the loaded class!!\");\n                e.printStackTrace();\n            }\n\n            // Remove the cached certificates and the certificate..\n            mSecureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n            Log.d(TAG_MAIN, \"Cached data of SecureDexClassLoader have been wiped out..\");\n\n        } catch (MalformedURLException e) {\n\n            // The previous entries for the map are not necessarily the right ones\n            // but still they are not malformed so no exception should be raised.\n            Log.e(TAG_MAIN, \"A malformed URL was provided for a remote certificate location\");\n\n        }\n    }\n\n    /**\n     * This method is used to set up and manage a DexClassLoader component in\n     * order to retrieve a new activity from an .apk, which has been\n     * already downloaded and installed on the mobile device.\n     * If everything works fine, it will instantiate the main activity of\n     * this .apk.\n     */\n    protected void setUpDexClassLoader() {\n\n        // First check: this operation can only start after\n        // that the proper button has just been pressed..\n        if (!effectiveDexClassLoader) return;\n\n        Log.d(TAG_MAIN, \"Setting up DexClassLoader..\");\n\n        if (PROFILING_ON) Debug.startMethodTracing(\"DexClassLoader\");\n\n        File dexOutputDir = getDir(\"dex\", MODE_PRIVATE);\n        DexClassLoader mDexClassLoader = new DexClassLoader(\n                exampleSignedAPKPath,\n                dexOutputDir.getAbsolutePath(),\n                null,\n                ClassLoader.getSystemClassLoader());\n\n        try {\n\n            // TODO(lfalsina)\n            // Use reflection to enforce the class loader of this activity\n            // to be the DexClassLoader one instantiated in this function.\n            // Reference: http://blog.pentests.pl/2015/02/android-dynamic-activities.html\n\n            // Load NasaDailyImage Main Activity..\n            Class<?> loadedClass = mDexClassLoader.loadClass(classNameInAPK);\n\n            if (PROFILING_ON) Debug.stopMethodTracing(); // end of \"DexClassLoader\" trace\n\n            final Activity NasaDailyActivity = (Activity) loadedClass.newInstance();\n\n            // Note that in this case loading class operation was performed even if the APK which contains\n            // the target class was signed just with the Android Debug key. This operation would have failed\n            // if SecureDexClassLoader would have been used in stead..\n            Log.i(TAG_MAIN, \"Found class: \" + loadedClass.getSimpleName() + \"; APK path: \" + exampleSignedAPKPath);\n\n            toastHandler.post(new Runnable() {\n\n                @Override\n                public void run() {\n                    Toast.makeText(\n                            MainActivity.this,\n                            \"DexClassLoader was successful! Found activity: \" + NasaDailyActivity.getClass().getName(),\n                            Toast.LENGTH_SHORT).show();\n                }\n\n            });\n\n            // An intent is defined to start the new loaded activity.\n            //Intent transitionIntent = new Intent(this, loadedClass);\n            //transitionIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            //this.startActivity(transitionIntent);\n\n        } catch (ClassNotFoundException e) {\n\n            Log.e(TAG_MAIN, \"Error: Class not found!\");\n\n            toastHandler.post(new Runnable() {\n\n                @Override\n                public void run() {\n                    Toast.makeText(MainActivity.this,\n                            \"Error! No class found for DexClassLoader..\",\n                            Toast.LENGTH_SHORT).show();\n                }\n\n            });\n\n            e.printStackTrace();\n        } catch (ActivityNotFoundException e) {\n\n            Log.e(TAG_MAIN, \"Error: Activity not found in the manifest!\");\n\n            toastHandler.post(new Runnable() {\n\n                @Override\n                public void run() {\n                    Toast.makeText(MainActivity.this,\n                            \"Error! The activity found by DexClassLoader is not a legitimate one..\",\n                            Toast.LENGTH_SHORT).show();\n                }\n\n            });\n\n            e.printStackTrace();\n        } catch (InstantiationException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "example/AS/app/src/main/res/layout/activity_dex_class_sample.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\"\n    tools:context=\"${packageName}.${activityClass}\" >\n\n    <Switch\n        android:id=\"@+id/switch_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:visibility=\"gone\"\n        android:text=\"@string/switch_dex_load_jar\" />\n\n    <TextView\n        android:id=\"@+id/exp_text_dex_load_jar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/explanation_dex_load_jar\"\n        android:gravity=\"center\"\n        android:textSize=\"20sp\" />\n    \n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n    \tandroid:layout_height=\"wrap_content\"\n    \tandroid:gravity=\"center\"\n    \tandroid:orientation=\"horizontal\"\n    \tstyle=\"?android:attr/buttonBarStyle\" >\n        \n        <Button\n        android:id=\"@+id/button1_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onBtnClick\"\n        android:text=\"@string/btn_dex_load_jar\" />\n        \n        <Button\n        android:id=\"@+id/button2_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onBtnClick\"\n        android:text=\"@string/btn_dex_load_jar\" />\n        \n        <Button\n        android:id=\"@+id/button3_dex_load_jar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"onBtnClickExit\"\n        android:visibility=\"gone\"\n        android:text=\"@string/btn_dex_load_jar\" />\n        \n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "example/AS/app/src/main/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\"\n    android:gravity=\"center\"\n\tandroid:layout_weight=\"0\"\n    tools:context=\".MainActivity\" >\n\n        <TextView \n\t   \tandroid:id=\"@+id/application_aim\"\n   \t    android:layout_width=\"match_parent\"\n    \tandroid:layout_height=\"wrap_content\"\n    \tandroid:gravity=\"center\"\n    \tandroid:text=\"@string/app_aim\"\n    \tandroid:textSize=\"16sp\"\n\t\t/>\n    \t\n\t\t<ListView\n    \tandroid:id=\"@+id/listview\"\n    \tandroid:layout_width=\"fill_parent\" \n    \tandroid:layout_height=\"wrap_content\"\n\t\t/>\n\n</LinearLayout>"
  },
  {
    "path": "example/AS/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">Dynamic Code Loading Techniques</string>\n    <string name=\"app_aim\">The aim of this simple application\n        is showing a couple of techniques which could be used\n        to load external code from a remote or a local path\n        dynamically.</string>\n    <string name=\"title_activity_dex_class_sample\">DexClassLoader with a .jar container</string>\n    <string name=\"explanation_dex_load_jar\">Here it is shown a small example\n        of a Java class, loaded dynamically through DexClassLoader from a jar container.\n        \\nIts methods are used to rearrange and modify the graphical components in this screen..\n        \\nGuess what you should do now ;)</string>\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"btn_dex_load_jar\">Click me!</string>\n    <string name=\"switch_dex_load_jar\">Switch</string>\n    <string name=\"title_activity_loaded_dynamically\">Dynamically loaded activity</string>\n\n</resources>\n"
  },
  {
    "path": "example/AS/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.5.0'\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n"
  },
  {
    "path": "example/AS/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-all.zip\n"
  },
  {
    "path": "example/AS/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "example/AS/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "example/AS/settings.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "gnr/.gitignore",
    "content": "/app/build/\n/build/\n/import-summary.txt\n.gradle\n/local.properties\n.DS_Store\n\n## Directory-based project format:\n.idea/\n\n## IntelliJ config files\n*.iml\n"
  },
  {
    "path": "gnr/app/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'maven'\n\next {\n    PUBLISH_GROUP_ID = 'it.necst.grabnrun'\n    PUBLISH_ARTIFACT_ID = 'grabnrun'\n    PUBLISH_VERSION = '1.0.4'\n}\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 21\n        versionCode 5\n        versionName \"1.0.4\"\n\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'\n        }\n    }\n\n    sourceSets {\n\n        androidTest {\n            java.srcDirs = ['test/java']\n        }\n    }\n\n    // Used to avoid unit tests to fail when encountering a static\n    // Android method not mocked.\n    // See: http://g.co/androidstudio/not-mocked\n    testOptions {\n        unitTests.returnDefaultValues = true\n    }\n}\n\ndependencies {\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile group: 'com.google.guava', name: 'guava', version: '18.0'\n\n    // Unit testing dependencies\n    testCompile 'junit:junit:4.12'\n    testCompile 'org.mockito:mockito-core:1.10.19'\n    testCompile 'org.hamcrest:hamcrest-library:1.1'\n    testCompile \"org.robolectric:robolectric:3.0\"\n}\n\ndef groupId = project.PUBLISH_GROUP_ID\ndef artifactId = project.PUBLISH_ARTIFACT_ID\ndef version = project.PUBLISH_VERSION\n\ndef localReleaseDest = \"${buildDir}/release/${version}\"\n\ntask androidJavadocs(type: Javadoc) {\n    source = android.sourceSets.main.java.srcDirs\n    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n}\n\ntask androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {\n    classifier = 'javadoc'\n    from androidJavadocs.destinationDir\n}\n\ntask androidSourcesJar(type: Jar) {\n    classifier = 'sources'\n    from android.sourceSets.main.java.srcDirs\n}\n\nuploadArchives {\n    repositories.mavenDeployer {\n        pom.groupId = groupId\n        pom.artifactId = artifactId\n        pom.version = version\n        pom.project {\n\n            name \"Grab'n Run Library\"\n            packaging 'aar'\n            description 'A simple and effective Java Library for Android projects to secure dynamic code loading.'\n            url 'https://github.com/lukeFalsina/Grab-n-Run'\n\n            licenses {\n                license {\n                    name 'The Apache Software License, Version 2.0'\n                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                    distribution 'repo'\n                }\n            }\n\n            developers {\n                developer {\n                    name 'Luca Falsina'\n                    email 'lfalsina@gmail.com'\n                }\n            }\n        }\n\n        repository(url: \"file://${localReleaseDest}\")\n    }\n}\n\ntask zipRelease(type: Zip) {\n    from localReleaseDest\n    destinationDir buildDir\n    archiveName \"release-${version}.zip\"\n}\n\ntask generateRelease << {\n    println \"Release ${version} can be found at ${localReleaseDest}/\"\n    println \"Release ${version} zipped can be found ${buildDir}/release-${version}.zip\"\n}\n\ngenerateRelease.dependsOn(uploadArchives)\ngenerateRelease.dependsOn(zipRelease)\n\nartifacts {\n    archives androidSourcesJar\n    archives androidJavadocsJar\n}"
  },
  {
    "path": "gnr/app/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "gnr/app/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n-assumenosideeffects class android.util.Log {\n    public static boolean isLoggable(java.lang.String, int);\n    public static int v(...);\n    public static int d(...);\n}\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "gnr/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"it.polimi.necst.gnr\" >\n    \n    <uses-permission android:name=\"android.permission.INTERNET\"></uses-permission>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"></uses-permission>\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"></uses-permission>\n\n</manifest>"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/CacheLogger.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\nimport static com.google.common.base.Preconditions.checkState;\n\nimport android.support.annotation.NonNull;\nimport android.util.Log;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport com.google.common.base.Optional;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Scanner;\n\n/**\n * {@link CacheLogger} is an helper class used by {@link SecureLoaderFactory} to keep lined\n * remote URLs with the corresponding local resources, which have been already cached in the\n * application private folder.\n * <p>\n * It provides methods to look for the associated local file of a remote URL, as well as adding a\n * new reference between a URL, and a local file. For dismissing it, invoke the finalizeLog()\n * method, which saves all the fresh references into an helper file on the device filesystem,\n * in the same private folder.\n * \n * @author Luca Falsina\n */\nfinal class CacheLogger {\n\n    private static final String TAG_CACHE_LOGGER = CacheLogger.class.getSimpleName();\n\t\n\t// Constants used for the formatting the helper log file\n\tprivate static final int ELEMENTS_PER_LOG_LINE = 3;\n\tprivate static final int REMOTE_URL_INDEX = 0;\n\tprivate static final int LOCAL_FILE_NAME_INDEX = 1;\n\tprivate static final int CREATION_TIMESTAMP_INDEX = 2;\n\tpublic static final int HOURS_PER_DAY = 24;\n\tpublic static final int MINUTES_PER_HOUR = 60;\n\tpublic static final int SEC_TO_MILLISEC = 1000;\n    @VisibleForTesting static final String FIELD_SEPARATOR_FOR_LOG_LINE = \" \";\n    @VisibleForTesting static final String TERMINATOR_FOR_LOG_LINE = \";\";\n\n    @VisibleForTesting static final String HELPER_FILE_NAME = \"helper.txt\";\n    public static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    private boolean hasBeenAlreadyFinalized;\n    private final String cacheDirectoryPath;\n\n    private final int daysTillConsideredFresh;\n    private final Map<URL, String> remoteURLToLocalFileNameMap;\n\tprivate final Map<URL, Long> remoteURLToCreationTimestampMap;\n\n    private File helperFile;\n\n\t/**\n\t * This constructor generates a {@link CacheLogger} instance which will\n\t * analyze the helper.txt file in the provided directory and \n\t * create all the references between remote URLs and local cached\n\t * resources.\n\t * <p>\n\t * All of those resources, whose life time is bigger than the one \n\t * stated by the daysTillConsideredFresh parameter, will be \n\t * automatically erased. \n\t * \n\t * @param cacheDirectoryPath\n\t *  the path to the directory which contains both the cached local\n\t *  resources and the Log helper file.\n\t * @param daysTillConsideredFresh\n\t *  the number of days till a resource will be considered fresh and so\n\t *  good to be cached.\n     * @throws IllegalArgumentException if the number of days is not greater then zero\n\t */\n\tCacheLogger(@NonNull String cacheDirectoryPath, int daysTillConsideredFresh) {\n\t\tthis.cacheDirectoryPath = checkNotNull(\n\t\t\t\tcacheDirectoryPath, \"cacheDirectoryPath was null.\");\n\t\tcheckArgument(\n\t\t\t\tdaysTillConsideredFresh > 0, \"daysTillConsideredFresh must be a positive integer.\");\n        this.daysTillConsideredFresh = daysTillConsideredFresh;\n\n\t\tthis.remoteURLToLocalFileNameMap = new HashMap<>();\n\t\tthis.remoteURLToCreationTimestampMap = new HashMap<>();\n\n\t\thasBeenAlreadyFinalized = false;\n\t\t\n\t\t// Check whether the \"helper\" file exists\n\t\t// In this case, populate the map accordingly to it.\n\t\t// Otherwise just skip this step.\n        initializeMapsThroughHelperFile();\n\t}\n\n    private void initializeMapsThroughHelperFile() {\n        helperFile = new File(cacheDirectoryPath, HELPER_FILE_NAME);\n\n        if (helperFile.exists()) {\n            Scanner helperFileScanner = null;\n\n            try {\n                helperFileScanner = new Scanner(helperFile)\n                        .useDelimiter(TERMINATOR_FOR_LOG_LINE + LINE_SEPARATOR);\n\n                while (helperFileScanner.hasNext()) {\n                    parseLineInHelperFile(helperFileScanner.next());\n                }\n            } catch (FileNotFoundException e) {\n                Log.w(TAG_CACHE_LOGGER, \"Issue while opening the helper file\");\n            } finally {\n                if (helperFileScanner != null) {\n                    helperFileScanner.close();\n                }\n            }\n        }\n    }\n\n    private void parseLineInHelperFile(String currentLine) {\n        checkNotNull(currentLine, \"The current parsed line was empty.\");\n        String[] lineTokens = currentLine.split(FIELD_SEPARATOR_FOR_LOG_LINE);\n\n        if (lineTokens.length == ELEMENTS_PER_LOG_LINE) {\n            File checkContainerFile = new File(cacheDirectoryPath, lineTokens[LOCAL_FILE_NAME_INDEX]);\n\n            if (checkContainerFile.exists()) {\n                try {\n                    // The associated file is present. Now it is necessary\n                    // to check whether it is fresh enough.\n                    checkLocalFileFreshness(Long.valueOf(lineTokens[CREATION_TIMESTAMP_INDEX]));\n\n                    // Cached file is fresh enough and it should be added to the hash maps.\n                    URL reconstructedRemoteURL = new URL(lineTokens[REMOTE_URL_INDEX]);\n                    remoteURLToLocalFileNameMap.put(\n                            reconstructedRemoteURL, lineTokens[LOCAL_FILE_NAME_INDEX]);\n                    remoteURLToCreationTimestampMap.put(\n                            reconstructedRemoteURL, Long.valueOf(lineTokens[CREATION_TIMESTAMP_INDEX]));\n                } catch (IllegalStateException unused) {\n                    // File is not fresh anymore so it should be erased..\n                    if (!checkContainerFile.delete())\n                        Log.w(TAG_CACHE_LOGGER,\n                                \"Issue while erasing \" + checkContainerFile.getAbsolutePath());\n                } catch (MalformedURLException e) {\n                    Log.w(\n                            TAG_CACHE_LOGGER,\n                            \"The string \" + lineTokens[REMOTE_URL_INDEX] + \" can not be \" +\n                                    \"converted back to a valid remote URL.\");\n                }\n            }\n        }\n    }\n\n    private void checkLocalFileFreshness(Long fileCreationTimestamp) {\n        long currentLivedTime = System.currentTimeMillis() - fileCreationTimestamp;\n        long maximumTimeToLive =\n\t\t\t\tdaysTillConsideredFresh * HOURS_PER_DAY * MINUTES_PER_HOUR * SEC_TO_MILLISEC;\n        checkState(currentLivedTime < maximumTimeToLive, \"The current local copy is not fresh enough.\");\n    }\n\n    /**\n\t * This method checks inside the {@link CacheLogger} data structure whether a certain URL of\n     * a remote container is associated to one local file stored in the private application folder.\n\t * \n\t * @param remoteURL\n\t *  a {@link java.net.URL} pointing to a remote resource.\n\t * @return\n\t *  an optional {@link java.lang.String} pointing to the local file associated to the remote URL.\n\t */\n\tfinal @NonNull Optional<String> checkForCachedEntry(@NonNull URL remoteURL) {\n        checkNotNull(remoteURL, \"The remote URL must not be null\");\n\t\tif (hasBeenAlreadyFinalized) return Optional.absent();\n\n\t\t// If the remote URL is contained in the map, return the\n\t\t// linked fresh local container\n\t\tif (remoteURLToLocalFileNameMap.containsKey(remoteURL))\n\t\t\tif (new File(cacheDirectoryPath, remoteURLToLocalFileNameMap.get(remoteURL)).exists())\n\t\t\t\treturn Optional.of(remoteURLToLocalFileNameMap.get(remoteURL));\n\t\t\n\t\t// Otherwise no cached entry..\n\t\treturn Optional.absent();\n\t}\n\t\n\t/**\n\t * Every time that a remote resource is successfully imported into the local\n\t * cache folder, this method should be invoked to link the initial remote \n\t * {@link java.net.URL} and the corresponding local file stored on the mobile.\n\t * \n\t * @param remoteURL\n\t *  the remote {@link java.net.URL} from which the resource was retrieved.\n\t * @param localFileName\n\t *  the name of the file on the mobile where the resource will be stored.\n\t */\n\tfinal void addCachedEntryToLog(@NonNull URL remoteURL, @NonNull String localFileName) {\n        checkNotNull(remoteURL, \"The remote URL must not be null\");\n        checkNotNull(localFileName, \"The file name  must not be null\");\n        if (hasBeenAlreadyFinalized) return;\n\n\t\t// Add also a timestamp for verifying the freshness of the new log entry later.\n\t\tremoteURLToCreationTimestampMap.put(remoteURL, System.currentTimeMillis());\n\t\tremoteURLToLocalFileNameMap.put(remoteURL, localFileName);\n\t}\n\n\t/**\n\t * This method must be called before dismissing the {@link CacheLogger} object.\n\t * <p>\n\t * It writes back to the helper file all the saved linkages between remote \n\t * {@link java.net.URL} and local resources imported into the application private\n\t * directory.\n\t */\n\tfinal void finalizeLog() {\n\t\tif (hasBeenAlreadyFinalized) return;\n\t\thasBeenAlreadyFinalized = true;\n\n        deletePreviousHelperFile();\n\n        if (!remoteURLToLocalFileNameMap.isEmpty()) {\n            updateHelperFileWithNewMappings();\n\t\t}\n\t}\n\n    private void deletePreviousHelperFile() {\n        if (helperFile.exists())\n            if (!helperFile.delete())\n                Log.w(TAG_CACHE_LOGGER, \"Problem while erasing old copy of helper file!\");\n    }\n\n    private void updateHelperFileWithNewMappings() {\n        PrintWriter mPrintWriter = null;\n\n        try {\n            mPrintWriter = new PrintWriter(helperFile);\n\n            for (URL currentRemoteURL : remoteURLToLocalFileNameMap.keySet()) {\n                if (remoteURLToCreationTimestampMap.containsKey(currentRemoteURL)) {\n                    mPrintWriter.println(getLogLineFromURL(currentRemoteURL));\n                }\n            }\n\n            if (mPrintWriter.checkError()) { throw new IOException(); }\n            Log.d(TAG_CACHE_LOGGER, \"Helper file was correctly stored on the device.\");\n        } catch (IOException e) {\n            Log.w(TAG_CACHE_LOGGER, \"Problem while storing the updated helper file.\");\n        } finally {\n            if (mPrintWriter != null)\n                mPrintWriter.close();\n        }\n    }\n\n    private String getLogLineFromURL(URL currentRemoteURL) {\n        return currentRemoteURL.toExternalForm() + FIELD_SEPARATOR_FOR_LOG_LINE\n                + remoteURLToLocalFileNameMap.get(currentRemoteURL) + FIELD_SEPARATOR_FOR_LOG_LINE\n                + remoteURLToCreationTimestampMap.get(currentRemoteURL) + TERMINATOR_FOR_LOG_LINE;\n    }\n}"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/CertificateFileFilterByNameMatch.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport android.support.annotation.NonNull;\n\nimport com.google.common.annotations.VisibleForTesting;\n\n/**\n * CertificateFileFilterByNameMatch checks local files and verifies whether\n * these elements hold the correct extension (.pem), and they match\n * the expected certificate name provided at construction time.\n * \n * @author Luca Falsina\n */\nfinal class CertificateFileFilterByNameMatch extends FileFilterByNameMatch {\n\n    @VisibleForTesting final static String PEM_EXTENSION = \".pem\";\n\t\n\t/**\n\t * A constructor for the filter which receives the \n\t * name of the desired certificate as a parameter.\n\t * <p>\n\t * Do not provide the extension of the certificate\n\t * file but only the name!\n\t * \n\t * @param certificateName\n\t *  the file name of the certificate.\n\t */\n\tCertificateFileFilterByNameMatch(@NonNull String certificateName) {\n\t\tsuper(\n                checkNotNull(certificateName, \"The input name for the certificate was null\"),\n                PEM_EXTENSION);\n\t}\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/ContainerSignatureVerifier.java",
    "content": "package it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\nimport static it.necst.grabnrun.FileHelper.APK_EXTENSION;\nimport static it.necst.grabnrun.FileHelper.JAR_EXTENSION;\nimport static it.necst.grabnrun.FileHelper.extractExtensionFromFilePath;\n\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.Signature;\nimport android.support.annotation.NonNull;\nimport android.util.Log;\n\nimport com.google.common.base.Optional;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateExpiredException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.CertificateNotYetValidException;\nimport java.security.cert.X509Certificate;\nimport java.util.Enumeration;\nimport java.util.Vector;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\n\nclass ContainerSignatureVerifier {\n    private static final String TAG_CONTAINER_SIGNATURE_VERIFIER =\n            ContainerSignatureVerifier.class.getSimpleName();\n\n    private final PackageManager packageManager;\n    private final CertificateFactory certificateFactory;\n\n    ContainerSignatureVerifier(\n            @NonNull PackageManager packageManager,\n            @NonNull CertificateFactory certificateFactory) {\n        this.packageManager = checkNotNull(packageManager);\n        this.certificateFactory = checkNotNull(certificateFactory);\n    }\n\n    // TODO(falsinal): It may be possible to simplify the logic here by\n    // removing the APK branch\n\n    // Given the path to a jar/apk container and a valid certificate instance this method returns\n    // whether the container is signed properly against the verified certificate.\n    boolean verifyContainerSignatureAgainstCertificate(\n            @NonNull String containerPath,\n            @NonNull X509Certificate trustedCertificate) {\n        checkNotNull(containerPath);\n        checkArgument(\n                !containerPath.isEmpty() && (new File(containerPath).exists()),\n                \"The container path must be associated to an existing file\");\n        checkNotNull(trustedCertificate);\n\n        // Check whether the selected resource is a jar or apk container\n        Optional<String> optionalExtension = extractExtensionFromFilePath(containerPath);\n        if (!optionalExtension.isPresent()) {\n            return false;\n        }\n        String extension = optionalExtension.get();\n\n        boolean signatureCheckIsSuccessful = false;\n\n        // Depending on the container extension the process for\n        // signature verification changes\n        if (extension.equals(APK_EXTENSION)) {\n\n            // APK container case:\n            // At first look for the certificates used to sign the apk\n            // and check whether at least one of them is the verified one..\n\n            PackageInfo mPackageInfo =\n                    packageManager.getPackageArchiveInfo(containerPath, PackageManager.GET_SIGNATURES);\n\n            if (mPackageInfo != null) {\n\n                // Use PackageManager field to retrieve the certificates used to sign the apk\n                Signature[] signatures = mPackageInfo.signatures;\n\n                if (signatures != null) {\n                    for (Signature sign : signatures) {\n                        if (sign != null) {\n\n                            X509Certificate certFromSign;\n                            InputStream inStream = null;\n\n                            try {\n\n                                // Recreate the certificate starting from this signature\n                                inStream = new ByteArrayInputStream(sign.toByteArray());\n                                certFromSign = (X509Certificate) certificateFactory.generateCertificate(inStream);\n\n                                // Check that the reconstructed certificate is not expired..\n                                certFromSign.checkValidity();\n\n                                // Check whether the reconstructed certificate and the trusted one match\n                                // Please note that certificates may be self-signed but it's not an issue..\n                                if (certFromSign.equals(trustedCertificate))\n                                    // This a necessary but not sufficient condition to\n                                    // prove that the apk container has not been repackaged..\n                                    signatureCheckIsSuccessful = true;\n\n                            } catch (CertificateException e) {\n                                // If this branch is reached certificateFromSign is not valid..\n                            } finally {\n                                if (inStream != null) {\n                                    try {\n                                        inStream.close();\n                                    } catch (IOException e) {\n                                        e.printStackTrace();\n                                    }\n                                }\n                            }\n                        }\n                    }\n                } else {\n                    Log.i(TAG_CONTAINER_SIGNATURE_VERIFIER, \"An invalid/corrupted signature is associated with the source archive.\");\n                }\n            } else {\n                Log.i(TAG_CONTAINER_SIGNATURE_VERIFIER, \"An invalid/corrupted container was found.\");\n            }\n        }\n\n        // This branch must be taken by all jar containers and by those apk containers\n        // whose certificates list contains also the trusted verified certificate.\n        if (extension.equals(JAR_EXTENSION) ||\n                (extension.equals(APK_EXTENSION) && signatureCheckIsSuccessful)) {\n\n            // Verify that each entry of the container has been signed properly\n            JarFile containerToVerify = null;\n\n            try {\n\n                containerToVerify = new JarFile(containerPath);\n                // This method will throw an IOException whenever\n                // the JAR container was not signed with the trusted certificate\n                // N.B. apk are an extension of a jar container..\n                verifyJARContainer(containerToVerify, trustedCertificate);\n\n                // No exception raised so the signature\n                // verification succeeded\n                signatureCheckIsSuccessful = true;\n\n            } catch (Exception e) {\n                // Signature process failed since it triggered\n                // an exception (either an IOException or a SecurityException)\n                signatureCheckIsSuccessful = false;\n\n            } finally {\n                if (containerToVerify != null)\n                    try {\n                        containerToVerify.close();\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n\n            }\n\n        }\n\n        return signatureCheckIsSuccessful;\n    }\n\n    private void verifyJARContainer(\n            @NonNull JarFile jarFile,\n            @NonNull X509Certificate trustedCertificate) throws IOException {\n\n        Vector<JarEntry> entriesVec = new Vector<>();\n\n        // Ensure the jar file is at least signed.\n        Manifest man = jarFile.getManifest();\n        if (man == null) {\n            Log.i(TAG_CONTAINER_SIGNATURE_VERIFIER, jarFile.getName() + \"is not signed.\");\n            throw new SecurityException(\"The container is not signed\");\n        }\n\n        // Ensure all the entries' signatures verify correctly\n        byte[] buffer = new byte[8192];\n        Enumeration<JarEntry> entries = jarFile.entries();\n\n        while (entries.hasMoreElements()) {\n\n            // Current entry in the jar container\n            JarEntry je = entries.nextElement();\n\n            // Skip directories.\n            if (je.isDirectory()) continue;\n            entriesVec.addElement(je);\n            InputStream inStream = jarFile.getInputStream(je);\n\n            // Read in each jar entry. A security exception will\n            // be thrown if a signature/digest check fails.\n            while (inStream.read(buffer, 0, buffer.length) != -1) {\n                // Don't care as soon as no exception is raised..\n            }\n\n            // Close the input stream\n            inStream.close();\n        }\n\n        // Get the list of signed entries from which certificates\n        // will be extracted..\n        Enumeration<JarEntry> signedEntries = entriesVec.elements();\n\n        while (signedEntries.hasMoreElements()) {\n\n            JarEntry signedEntry = signedEntries.nextElement();\n\n            // Every file must be signed except files in META-INF.\n            Certificate[] certificates = signedEntry.getCertificates();\n            if ((certificates == null) || (certificates.length == 0)) {\n                if (!signedEntry.getName().startsWith(\"META-INF\")) {\n                    Log.i(TAG_CONTAINER_SIGNATURE_VERIFIER, signedEntry.getName() + \" is an unsigned class file\");\n                    throw new SecurityException(\"The container has unsigned class files.\");\n                }\n            }\n            else {\n                // Check whether the file is signed by the expected\n                // signer. The jar may be signed by multiple signers.\n                // So see if one of the signers is 'trustedCert'.\n                boolean signedAsExpected = false;\n\n                for (Certificate signerCert : certificates) {\n\n                    try {\n\n                        ((X509Certificate) signerCert).checkValidity();\n                    } catch (CertificateExpiredException\n                            | CertificateNotYetValidException e) {\n                        // Usually expired certificate are not such a relevant issue; nevertheless\n                        // on Android a common practice is using certificates (even self signed) but\n                        // with at least a long life span and so temporal validity should be enforced..\n                        Log.i(TAG_CONTAINER_SIGNATURE_VERIFIER, \"One of the certificates used to sign \" + signedEntry.getName() + \" is expired\");\n                        throw new SecurityException(\"One of the used certificates is expired!\");\n                    } catch (Exception e) {\n                        // It was impossible to cast the general certificate into an X.509 one..\n                    }\n\n                    if (signerCert.equals(trustedCertificate))\n                        // The trusted certificate was used to sign this entry\n                        signedAsExpected = true;\n                }\n\n                if (!signedAsExpected) {\n                    Log.i(TAG_CONTAINER_SIGNATURE_VERIFIER, \"The trusted certificate was not used to sign \" + signedEntry.getName());\n                    throw new SecurityException(\"The provider is not signed by a trusted signer\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/DexPathStringProcessor.java",
    "content": "package it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport android.support.annotation.NonNull;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.NoSuchElementException;\nimport java.util.regex.Pattern;\n\nclass DexPathStringProcessor {\n    private static final String HTTP_PROTOCOL_STRING = \"http://\";\n    private static final String HTTPS_PROTOCOL_STRING = \"https://\";\n    private static final String HTTP_PROTOCOL_STRING_WITHOUT_COLON = \"http//\";\n    private static final String HTTPS_PROTOCOL_STRING_WITHOUT_COLON = \"https//\";\n\n    private final List<String> extractedSingleDexPathStrings;\n    private final ListIterator<String> extractedSingleDexPathStringsIterator;\n\n    DexPathStringProcessor(@NonNull String fullDexPathString) {\n\n        checkNotNull(fullDexPathString, \"The dex path string must not be null\");\n        checkArgument(!fullDexPathString.isEmpty(), \"The dex path string must not be empty\");\n\n        extractedSingleDexPathStrings = extractSingleDexPathStringsFromFullOne(fullDexPathString);\n\n        extractedSingleDexPathStringsIterator = extractedSingleDexPathStrings.listIterator();\n    }\n\n    private List<String> extractSingleDexPathStringsFromFullOne(String fullDexPathString) {\n        // Necessary workaround to avoid remote URL being split in a wrong way..\n        String temporaryDexPathCompleteString = fullDexPathString\n                .replaceAll(HTTP_PROTOCOL_STRING, HTTP_PROTOCOL_STRING_WITHOUT_COLON)\n                .replaceAll(HTTPS_PROTOCOL_STRING, HTTPS_PROTOCOL_STRING_WITHOUT_COLON);\n\n        return Arrays.asList(\n                temporaryDexPathCompleteString.split(Pattern.quote(File.pathSeparator)));\n    }\n\n    public boolean hasNextDexPathString() {\n        return extractedSingleDexPathStringsIterator.hasNext();\n    }\n\n    public String nextDexPathString() throws NoSuchElementException {\n        String nextDexPathString = extractedSingleDexPathStringsIterator.next();\n\n        if (nextDexPathString.startsWith(HTTP_PROTOCOL_STRING_WITHOUT_COLON)) {\n            return nextDexPathString.replaceFirst(\n                    HTTP_PROTOCOL_STRING_WITHOUT_COLON, HTTP_PROTOCOL_STRING);\n        }\n\n        if (nextDexPathString.startsWith(HTTPS_PROTOCOL_STRING_WITHOUT_COLON)) {\n            return nextDexPathString.replaceFirst(\n                    HTTPS_PROTOCOL_STRING_WITHOUT_COLON, HTTPS_PROTOCOL_STRING);\n        }\n\n        return nextDexPathString;\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/FileDownloader.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport static android.content.Context.*;\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport android.content.Context;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.support.annotation.NonNull;\nimport android.util.Log;\n\nimport com.google.common.base.Optional;\nimport com.google.common.collect.ImmutableMap;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\n\nimport javax.net.ssl.HttpsURLConnection;\n\n/**\n * {@link FileDownloader} retrieves remote resources like containers or certificates,\n * which are then used for dynamic class loading or signature verification.\n * \n * @author Luca Falsina\n */\nfinal class FileDownloader {\n\n    // Unique identifier used for Log entries\n\tprivate static final String TAG_FILE_DOWNLOADER = FileDownloader.class.getSimpleName();\n\n    public static final ImmutableMap<String, String> SUPPORTED_MIME_TYPE_TO_FILE_EXTENSION_MAP =\n            ImmutableMap.of(\n                    \"application/vnd.android.package-archive\", \".apk\",\n                    \"application/java-archive\", \".jar\",\n                    \"application/octet-stream\", \".pem\");\n\n    private static final String HTTPS_PROTOCOL_AS_STRING = \"https\";\n    private static final String HTTP_PROTOCOL_AS_STRING = \"http\";\n\n    // Objects used to check availability of Internet connection\n\tprivate ConnectivityManager mConnectivityManager;\n\n\t// private NetworkInfo activeNetworkInfo;\n\tprivate Optional<String> retrievedFileMimeType;\n\n\t/**\n\t * This constructor initializes a {@link FileDownloader} for retrieving remote resources.\n\t *\n\t * @param parentContext\n\t *  a {@link Context} coming from the parent {@link android.app.Activity} and used to\n\t *  retrieve the state of the connectivity service on the mobile.\n\t */\n\tFileDownloader(@NonNull Context parentContext) {\n\n        checkNotNull(parentContext, \"The parent context must be not null\");\n\t\tmConnectivityManager = (ConnectivityManager) parentContext.getSystemService(CONNECTIVITY_SERVICE);\n\t\tretrievedFileMimeType = Optional.absent();\n\t}\n\n\t/**\n\t * This method takes a remote {@link URL}, downloads the corresponding resource,\n     * and stores it on the mobile at the location provided by the second parameter.\n     * The download task is carried on a separate thread.\n\t * <p>\n\t * It is also possible to specify whether redirect link should be followed for one hop,\n     * or simply ignored.\n\t * \n\t * @param remoteURL\n\t *  the {@link URL} where the remote resource is located.\n\t * @param localURI\n\t *  the local path where the final resource will be stored in case of a successful download.\n\t * @param isRedirectAllowed\n\t *  a {@code boolean} stating whether {@link FileDownloader} should follow redirect link\n     *  for one hop, or not.\n\t * @return\n\t *  a boolean indicating whether the download was successful.\n\t */\n\tfinal boolean downloadRemoteResource(\n            @NonNull final URL remoteURL,\n            @NonNull final String localURI,\n            final boolean isRedirectAllowed) {\n\n        checkNotNull(\n                remoteURL,\n                \"The URL at which the remote resource is located must not be null\");\n        checkNotNull(\n                localURI,\n                \"The URI at which storing the file pointed by the remote URL must not be null\");\n        checkArgument(\n                remoteURL.getProtocol().equalsIgnoreCase(HTTPS_PROTOCOL_AS_STRING) ||\n                        remoteURL.getProtocol().equalsIgnoreCase(HTTP_PROTOCOL_AS_STRING),\n                \"The FileDownloader supports only HTTP, and HTTPS protocols\");\n\n\t\t// Check whether Internet access is granted..\n\t\tNetworkInfo activeNetworkInfo = mConnectivityManager.getActiveNetworkInfo();\n\t\tif (activeNetworkInfo == null || !activeNetworkInfo.isConnected()) {\n\t\t\t\n\t\t\tLog.w(TAG_FILE_DOWNLOADER, \"No connectivity is available. Download failed!\");\n\t\t\treturn false;\n\t\t}\n\t\t\n    \t// Data are retrieved in a separate thread.\n    \tThread dataThread = new Thread() {\n    \t\t\n    \t\t@Override\n    \t\tpublic void run() {\n    \t\t\t\n    \t\t\tHttpURLConnection urlConnection = null;\n    \t\t\tInputStream inputStream = null;\n    \t\t\tOutputStream outputStream = null;\n    \t\t\t\n    \t\t\ttry {\n\n    \t\t\t\turlConnection = openAndReturnConnectionAssociatedToURL(remoteURL);\n\n    \t\t\t\t// Fix timeout for the connection..\n    \t\t\t\turlConnection.setConnectTimeout(1000);\n    \t\t\t\t\n    \t\t\t\tLog.d(TAG_FILE_DOWNLOADER, \"A connection was set up: \" + remoteURL.toString());\n    \t\t\t\t\n    \t\t\t\t// When it is allowed, check if this URL asks for a redirect..\n    \t\t\t\tif (isRedirectAllowed) {\n    \t\t\t\t\t\n    \t\t\t\t\t// Normally 301, 302, or 303 is redirect..\n    \t\t\t\t\tint connection_status = urlConnection.getResponseCode();\n\n    \t\t\t\t\tif (connection_status != HttpURLConnection.HTTP_OK) {\n    \t\t\t\t\t\tif (connection_status == HttpURLConnection.HTTP_MOVED_TEMP\n    \t\t\t\t\t\t\t|| connection_status == HttpURLConnection.HTTP_MOVED_PERM\n    \t\t\t\t\t\t\t\t|| connection_status == HttpURLConnection.HTTP_SEE_OTHER) {\n                                urlConnection = followURLRedirection(urlConnection);\n                            }\n                        }\n    \t\t\t\t}\n\n                    if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {\n\n                        retrievedFileMimeType = Optional.fromNullable(urlConnection.getContentType());\n\n                        inputStream = new BufferedInputStream(urlConnection.getInputStream());\n                        outputStream = new FileOutputStream(localURI);\n\n                        copyReadContentFromInputStreamToOutputStream(inputStream, outputStream);\n                    }\n\n    \t\t\t} catch (IOException e) {\n    \t\t\t\t// No file was found at the remote URL!\n    \t\t\t\t// Nothing should have been written at the local path \n    \t\t\t\t// and so false should be returned.\n    \t\t\t} finally {\n                    closeConnectionAndStreams(urlConnection, inputStream, outputStream);\n    \t\t\t}\n    \t\t}\n\n            private HttpURLConnection followURLRedirection(HttpURLConnection urlConnection)\n                    throws IOException {\n                // Get redirect URL from \"location\" header field\n                URL redirectedURL = new URL(urlConnection.getHeaderField(\"Location\"));\n\n                // Get the cookie for login if provided\n                String cookies = urlConnection.getHeaderField(\"Set-Cookie\");\n\n                // Open the new redirected connection again..\n                urlConnection = openAndReturnConnectionAssociatedToURL(redirectedURL);\n\n                urlConnection.setConnectTimeout(1000);\n                urlConnection.setRequestProperty(\"Cookie\", cookies);\n\n                Log.d(TAG_FILE_DOWNLOADER, \"The connection was redirected to: \" + redirectedURL.toString());\n                return urlConnection;\n            }\n\n            private HttpURLConnection openAndReturnConnectionAssociatedToURL(URL remoteURL)\n                    throws IOException {\n                if (remoteURL.getProtocol().equals(HTTPS_PROTOCOL_AS_STRING)) {\n                    return (HttpsURLConnection) remoteURL.openConnection();\n                } else {\n                    return (HttpURLConnection) remoteURL.openConnection();\n                }\n            }\n\n            private void copyReadContentFromInputStreamToOutputStream(\n                    InputStream inputStream,\n                    OutputStream outputStream) throws IOException {\n                int read;\n                byte[] bytes = new byte[1024];\n\n                while ((read = inputStream.read(bytes)) > 0) {\n                    outputStream.write(bytes, 0, read);\n                }\n\n                Log.i(TAG_FILE_DOWNLOADER, \"Download complete. Container Path: \" + localURI);\n            }\n\n            private void closeConnectionAndStreams(\n                    HttpURLConnection urlConnection,\n                    InputStream inputStream,\n                    OutputStream outputStream) {\n                Log.d(TAG_FILE_DOWNLOADER, \"Clean up all pending streams..\");\n                if (urlConnection != null)\n                    urlConnection.disconnect();\n\n                if (inputStream != null) {\n                    try {\n                        inputStream.close();\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                }\n\n                if (outputStream != null) {\n                    try {\n                        // outputStream.flush();\n                        outputStream.close();\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        };\n    \t\n    \tdataThread.start();\n    \t\n    \ttry {\n\t\t\tdataThread.join();\n\t\t} catch (InterruptedException e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t}\n\n        // Download is successful when a not empty file was retrieved, and stored.\n        File fileAtLocalURI = new File(localURI);\n        return fileAtLocalURI.exists() && fileAtLocalURI.length() > 0;\n\t}\n\t\n\t/**\n\t * This method analyzes the MIME-type of the last downloaded file\n\t * and returns the related extension.\n\t * \n\t * @return\n\t *  an optional with either the extension of the last downloaded file,\n     *  or absent if MIME-type is unknown\n     *  (see {@link #SUPPORTED_MIME_TYPE_TO_FILE_EXTENSION_MAP}).\n\t */\n\tOptional<String> getDownloadedFileExtension() {\n\n        if (retrievedFileMimeType.isPresent() &&\n                SUPPORTED_MIME_TYPE_TO_FILE_EXTENSION_MAP.containsKey(retrievedFileMimeType.get())) {\n            return Optional.of(\n                    SUPPORTED_MIME_TYPE_TO_FILE_EXTENSION_MAP.get(retrievedFileMimeType.get()));\n        }\n\n        return Optional.absent();\n\t}\n}"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/FileFilterByNameMatch.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport android.support.annotation.NonNull;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.util.Locale;\n\n/**\n * A simple implementation of the {@link FileFilter} interface that will accept\n * only those files whose name and extension match the required one\n * stated during object creation.\n * \n * @author Luca Falsina\n */\nclass FileFilterByNameMatch implements FileFilter {\n    private static final String DOT_FOLLOWED_BY_THREE_OR_FOUR_CHARACTERS = \".[a-z]{3,4}\";\n    private final String name;\n\tprivate final String extension;\n\n\t/**\n\t * Instantiate a {@link FileFilterByNameMatch} that will accept\n\t * files matching the provided name and extension.\n\t * \n\t * @param name\n\t *  the file name.\n\t * @param extension\n\t *  the file extension in the format \".????\" (e.g., \".txt\", or \".DOCX\").\n\t */\n\tFileFilterByNameMatch(@NonNull String name, @NonNull String extension) {\n\t\tthis.name = checkNotNull(name, \"The name of the target file was null\");\n\t\tcheckArgument(!name.isEmpty(), \"The file name must not be empty\");\n\t\tthis.extension = checkNotNull(extension, \"The extension of the target file was null\");\n        checkArgument(\n                extension.toLowerCase(Locale.US).matches(DOT_FOLLOWED_BY_THREE_OR_FOUR_CHARACTERS),\n                \"The extension must be one dot, followed by three, \" +\n                        \"or fours alphabetical characters\");\n\t}\n\t\n\t@Override\n\tpublic final boolean accept(@NonNull File file) {\n\t\tcheckNotNull(file, \"The input file descriptor was null\");\n\t\tif (file.isDirectory())\n\t\t\treturn false;\n\t\telse if (file.isFile()) {\n\t\t\tif (file.getName().equals(name + extension))\n\t\t    \t  return true;\n\t\t}\n\t\t\t\t\n\t\t// Used for any other kind of weird stuff reaching the filter..\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/FileHelper.java",
    "content": "package it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport android.support.annotation.NonNull;\n\nimport com.google.common.base.Optional;\n\nimport java.io.File;\n\npublic class FileHelper {\n    public static final String DOT_SEPARATOR = \".\";\n    public static final String APK_EXTENSION = DOT_SEPARATOR + \"apk\";\n    public static final String JAR_EXTENSION = DOT_SEPARATOR + \"jar\";\n\n    public static String extractFilePathWithoutExtensionFromFilePath(@NonNull String filePath) {\n        checkPreconditionsOnFilePath(filePath);\n\n        return extractSubstringWithoutExtensionFromString(filePath);\n    }\n\n    public static String extractFileNameWithoutExtensionFromFilePath(@NonNull String filePath) {\n        checkPreconditionsOnFilePath(filePath);\n\n        return extractSubstringWithoutExtensionFromString(extractFileNameFromFilePath(filePath));\n    }\n\n    private static String extractSubstringWithoutExtensionFromString(@NonNull String candidateString) {\n        int firstCharacterOfAnExtensionIndex = candidateString.lastIndexOf(DOT_SEPARATOR);\n\n        if (firstCharacterOfAnExtensionIndex > 0) {\n            return candidateString.substring(0, firstCharacterOfAnExtensionIndex);\n        } else {\n            return candidateString;\n        }\n    }\n\n    public static String extractFileNameFromFilePath(@NonNull String filePath) {\n        checkPreconditionsOnFilePath(filePath);\n\n        int lastOccurrenceOfFileSeparatorIndex = filePath.lastIndexOf(File.separator);\n\n        if (lastOccurrenceOfFileSeparatorIndex == -1)\n            return filePath;\n\n        return filePath.substring(lastOccurrenceOfFileSeparatorIndex + 1);\n    }\n\n    public static Optional<String> extractExtensionFromFilePath(@NonNull String filePath) {\n        checkPreconditionsOnFilePath(filePath);\n\n        int initialCharacterOfAnExtensionIndex = filePath.lastIndexOf(DOT_SEPARATOR);\n\n        if (initialCharacterOfAnExtensionIndex == -1)\n            return Optional.absent();\n\n        return Optional.of(filePath.substring(initialCharacterOfAnExtensionIndex));\n    }\n\n    public static boolean endsWithJarOrApkExtension(@NonNull String filePath) {\n        checkPreconditionsOnFilePath(filePath);\n\n        int initialCharacterOfAnExtensionIndex = filePath.lastIndexOf(DOT_SEPARATOR);\n\n        if (initialCharacterOfAnExtensionIndex == -1)\n            return false;\n\n        String fileExtension = filePath.substring(initialCharacterOfAnExtensionIndex);\n\n        return fileExtension.equalsIgnoreCase(APK_EXTENSION) ||\n                fileExtension.equalsIgnoreCase(JAR_EXTENSION);\n    }\n\n    private static void checkPreconditionsOnFilePath(@NonNull String filePath) {\n        checkNotNull(filePath, \"The path of the input file must not be null\");\n        checkArgument(!filePath.isEmpty(), \"The path of the input file must not be empty\");\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/PackageNameHelper.java",
    "content": "package it.necst.grabnrun;\n\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\n\nimport android.support.annotation.NonNull;\n\nimport com.google.common.annotations.VisibleForTesting;\n\nimport java.net.MalformedURLException;\nimport java.net.URL;\n\npublic class PackageNameHelper {\n    @VisibleForTesting static final String HTTPS_PROTOCOL_AS_STRING = \"https\";\n    @VisibleForTesting static final String DEFAULT_CERTIFICATE_FILE_NAME = \"/certificate.pem\";\n\n    private static final String DOT_AS_REGULAR_EXPRESSION = \"\\\\.\";\n    private static final int MINIMUM_NUMBER_OF_FIELDS_IN_A_PACKAGE_NAME = 2;\n    private static final int FIRST_LEVEL_DOMAIN_INDEX = 0;\n    private static final int SECOND_LEVEL_DOMAIN_INDEX = 1;\n\n    public static boolean isAValidPackageName(@NonNull String candidatePackageName) {\n        checkNotNull(candidatePackageName, \"The candidate package name must not be null\");\n\n        if (candidatePackageName.isEmpty()) return false;\n        if (candidatePackageName.endsWith(\".\")) return false;\n\n        String[] packageNameFields = candidatePackageName.split(DOT_AS_REGULAR_EXPRESSION);\n        if (packageNameFields.length < MINIMUM_NUMBER_OF_FIELDS_IN_A_PACKAGE_NAME) {\n            return false;\n        }\n\n        for (String packageNameField : packageNameFields) {\n            if (packageNameField.isEmpty()) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public static URL revertPackageNameToURL(@NonNull String candidatePackageName)\n            throws MalformedURLException {\n        checkArgument(\n                isAValidPackageName(candidatePackageName),\n                \"A valid package name must be provided\");\n\n        // If a package name is valid, it has at least two fields.\n        String[] packageNameFields = candidatePackageName.split(DOT_AS_REGULAR_EXPRESSION);\n        StringBuilder urlAsStringBuilder = new StringBuilder(HTTPS_PROTOCOL_AS_STRING + \"://\")\n                .append(packageNameFields[SECOND_LEVEL_DOMAIN_INDEX])\n                .append(\".\")\n                .append(packageNameFields[FIRST_LEVEL_DOMAIN_INDEX]);\n\n        for (int fieldIndex = 2; fieldIndex < packageNameFields.length; fieldIndex++) {\n            urlAsStringBuilder.append(\"/\")\n                    .append(packageNameFields[fieldIndex]);\n        }\n\n        urlAsStringBuilder.append(DEFAULT_CERTIFICATE_FILE_NAME);\n\n        return new URL(urlAsStringBuilder.toString());\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/PackageNameTrie.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport android.util.Log;\n\nimport com.google.common.base.Optional;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * {@link PackageNameTrie} is an helper class used to keep a hierarchy among package names and certificates.\n * <p>\n * In particular when a certificate is associated to a package name, all the descendant package names of the \n * previous one should be able to verify the signature of their classes with the same certificate.\n * <p>\n * Example:<br>\n * Package name \"com.example\" has certificate \"CertA\" associated to it.<br>\n * Package names \"com.example.polimi\" and \"com.example.application.system\" can use \"CertA\" as well.<br>\n * Package names \"com\" and \"it.example\" cannot use \"CertA\" for their classes verification.\n * \n * @author Luca Falsina\n */\nfinal class PackageNameTrie {\n\n\t// Unique identifier used for Log entries\n\tprivate static final String TAG_PACKAGE_NAME_TRIE = PackageNameTrie.class.getSimpleName();\n\t\n\t// Map which links the package name string to the associated node\n\tprivate Map<String, Boolean> packageNameToHasCertificateMap;\n\t\n\t/**\n\t * Basic constructor that initializes internal data structures.\n\t */\n\tPackageNameTrie() {\n\t\t\n\t\tpackageNameToHasCertificateMap = new HashMap<>();\n\t\t// Add also the root entry and suppose that it has \n\t\t// always an associated certificate.\n\t\tpackageNameToHasCertificateMap.put(\"\", true);\n\t}\n\t\n\t/**\n\t * This method generates an internal hierarchy representation of a provided package name.\n\t * In particular it will generate all not empty prefixes of the input package name.\n\t * <p>\n\t * Example execution:<br>\n\t * Input: \"com.example.polimi.application\"<br>\n\t * Internal Hierarchy generates the strings:<br>\n\t * \"com.example.polimi.application\", \"com.example.polimi\", \"com.example\" and \"com\".\n\t * \n\t * @param packageName\n\t *  the package name for which the hierarchical structure of package name prefix will be generated.\n\t */\n\tfinal void generateEntriesForPackageName(String packageName) {\n\t\t\n\t\tString currentPackageName = packageName;\n\t\tboolean hasFoundAnAlreadyInsertedPackageName = false;\n\t\t\n\t\twhile (!hasFoundAnAlreadyInsertedPackageName) {\n\t\t\t\n\t\t\tif (packageNameToHasCertificateMap.containsKey(currentPackageName)) {\n\t\t\t\t\n\t\t\t\t// In this case this entry has been already inserted in the map\n\t\t\t\t// so the process of package name generation stops here.\n\t\t\t\thasFoundAnAlreadyInsertedPackageName = true;\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\t// Need to insert this entry by populating the map accordingly\n\t\t\t\tpackageNameToHasCertificateMap.put(currentPackageName, false);\n\t\t\t\t\n\t\t\t\tLog.d(TAG_PACKAGE_NAME_TRIE, \"Inserted a new entry for \" + currentPackageName);\n\t\t\t\t\n\t\t\t\t// Now remove the last part of the package name and then\n\t\t\t\t// repeat the previous step recursively.\n\t\t\t\tcurrentPackageName = getUpALevel(currentPackageName);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t// Remove the last part of the package name.\n\t// If nothing more is left after this removal, return an empty string.\n\tprivate String getUpALevel(String packageName) {\n\t\t\n\t\tint lastPointIndex = packageName.lastIndexOf('.');\n\t\t\n\t\tif (lastPointIndex > 0)\n\t\t\treturn packageName.substring(0, lastPointIndex);\n\t\telse\n\t\t\treturn \"\";\n\t}\n\t\n\t/**\n\t * This method simply marks the input package name as one having an\n\t * associated certificate that may be used for verifying a container signature.\n     * <p>\n     * If the input package name has not been generated yet using\n     * {@link #generateEntriesForPackageName}, this method has no effect.\n\t * \n\t * @param packageName\n\t *  the package name to mark as having a certificate associated to it.\n\t */\n\tfinal void setPackageNameHasAssociatedCertificate(String packageName) {\n\t\t\n\t\tif (packageNameToHasCertificateMap.containsKey(packageName)) {\n\t\t\t\n\t\t\tpackageNameToHasCertificateMap.put(packageName, true);\n\t\t\tLog.d(TAG_PACKAGE_NAME_TRIE, packageName + \" has a certificate associated now.\");\n\t\t}\n\t}\n\t\n\t/**\n\t * This method is used to query {@link PackageNameTrie} to obtain the name of the\n\t * closest prefix of the input package name, which has been linked to a certificate.\n\t * \n\t * @param packageName\n\t *  the package name for which a certificate for signature verification must be found\n\t * @return\n\t *  an optional containing either the longest prefix of the input package name\n     *  (possibly the one provided as input), which has an associated certificate\n     *  for signature verification, or an absent value if no certificate\n\t *  is associated to any of the package names in its hierarchy.\n\t */\n\tfinal Optional<String> getPackageNameWithAssociatedCertificate(String packageName) {\n\t\t\n\t\tString currentPackageName = packageName;\n\t\t\n\t\tif (!packageNameToHasCertificateMap.containsKey(currentPackageName))\n\t\t\treturn Optional.absent();\n\t\t\n\t\twhile (!packageNameToHasCertificateMap.get(currentPackageName)) {\n            currentPackageName = getUpALevel(currentPackageName);\n\n            if (currentPackageName.equals(\"\")) {\n                Log.d(TAG_PACKAGE_NAME_TRIE, \"There is no prefix associated with a certificate \"\n                        + \"for package name \" + packageName);\n\n                return Optional.absent();\n            }\n        }\n\n\t\tLog.d(TAG_PACKAGE_NAME_TRIE, currentPackageName + \" is the closest package name to the target \"\n\t\t+ packageName + \" with an associated certificate for verification.\");\n\t\t\n\t\treturn Optional.of(currentPackageName);\n\t}\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/SecureDexClassLoader.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport static android.content.Context.MODE_PRIVATE;\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\nimport static dalvik.system.DexFile.loadDex;\nimport static it.necst.grabnrun.FileHelper.APK_EXTENSION;\nimport static it.necst.grabnrun.FileHelper.JAR_EXTENSION;\nimport static it.necst.grabnrun.FileHelper.extractExtensionFromFilePath;\nimport static it.necst.grabnrun.FileHelper.extractFilePathWithoutExtensionFromFilePath;\nimport static it.necst.grabnrun.SecureLoaderFactory.IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME;\nimport static it.necst.grabnrun.SecureLoaderFactory.X_509_CERTIFICATE;\nimport static java.util.Collections.synchronizedMap;\nimport static java.util.Collections.synchronizedSet;\n\nimport android.content.Context;\nimport android.support.annotation.NonNull;\nimport android.util.Log;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport com.google.common.base.Optional;\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.ImmutableSet;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateExpiredException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.CertificateNotYetValidException;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.jar.JarFile;\n\nimport javax.security.auth.x500.X500Principal;\n\nimport dalvik.system.DexClassLoader;\nimport dalvik.system.DexFile;\n\n/**\n * A class that provides an extension of default {@link dalvik.system.DexClassLoader} \n * provided by the Android system and it is used to load classes \n * from jar and apk container files including a classes.dex entry in a secure way.\n * <p>\n * In order to instantiate this class a call to \n * {@link SecureLoaderFactory#createDexClassLoader(String, String, ClassLoader, Map)} \n * must be performed.\n * <p>\n * {@link SecureDexClassLoader} ensures integrity of loaded external remote \n * classes by comparing them with the developer certificate, which\n * is retrieved either by a provided associative map between package names \n * and certificate remote URL or by simply reverting the \n * first two words of the package name of the loaded class and then \n * by adding each following word in the same order and separated by \n * a slash \"/\".\n * <p>\n * Package name reversion example:<br>\n * Class name = it.necst.grabnrun.example.TestClassImpl<br>\n * Constructed URL = https://necst.it/grabnrun/example<br>\n * Final certificate location = https://necst.it/grabnrun/example/certificate.pem<br>\n * <p>\n * A request is pointed to the final certificate location and if \n * the file is found, it is imported in the local private \n * application directory.\n * <p>\n * Please note that in the current implementation certificates obtained \n * by reverting package name must have been saved at the described \n * location as \"certificate.pem\". Moreover all the certificates must \n * fit requirements of a standard {@link java.security.cert.X509Certificate}, \n * they must be valid in the current time frame and of course they must have been \n * used to sign the jar or apk, which contains the classes to be loaded.\n * <p>\n * If any of these previous requirements is violated no class is loaded \n * and this class returns without executing any class loading operation.\n * \n * @author Luca Falsina\n */\npublic class SecureDexClassLoader {\n\n    // Unique identifier used for Log entries\n\tprivate static final String TAG_SECURE_DEX_CLASS_LOADER = SecureDexClassLoader.class.getSimpleName();\n\n    // Final name of the folder user to store certificates for the verification\n    @VisibleForTesting static final String IMPORTED_CERTIFICATE_PRIVATE_DIRECTORY_NAME = \"valid_certs\";\n\n    // Constant used to tune concurrent vs standard verification in Eager mode.\n    private static final int MINIMUM_NUMBER_OF_CONTAINERS_FOR_CONCURRENT_VERIFICATION = 2;\n\n    // Sets the Time Unit to milliseconds.\n    private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.MILLISECONDS;\n\n    // Relevant file entries, and file extensions\n    private static final String CLASSES_DEX_ENTRY_NAME = \"classes.dex\";\n    private static final String ODEX_EXTENSION = \".odex\";\n    private static final String PEM_EXTENSION = \".pem\";\n\n    private File certificateFolder, containersFolder;\n    private FileDownloader fileDownloader;\n    private CertificateFactory certificateFactory;\n    private final ContainerSignatureVerifier containerSignatureVerifier;\n\n\t// The internal DexClassLoader used to load classes that passes all the checks..\n\tprivate final DexClassLoader dexClassLoader;\n\n    private final Map<String, String> packageNameToContainerPathMap;\n    private final ImmutableMap<String, URL> packageNameToCertificateMap;\n\n\t// Used to verify if a call to the wiped out method has been performed.\n\tprivate boolean hasBeenWipedOut;\n\n\t// Variable used to understand whether SecureDexClassLoader will immediately verify all\n\t// the incoming containers or if it will just verify each one of those lazily when the\n\t// loadClass() method will be invoked.\n\tprivate boolean performLazyEvaluation;\n\t\n\t// Helper cache set used in lazy mode in order to check only once that the container\n\t// associated to a package name is valid (This works fine since each used container is\n\t// previously imported in an application-private folder).\n\tprivate final Set<String> lazyAlreadyVerifiedPackageNameSet;\n\t\n\t// An helper data structure used to connect each package name of a possible target class\n\t// to the certificate of the closest package name. The closeness relation here is considered\n\t// in terms of hierarchy on the package name.\n\tprivate PackageNameTrie packageNameTrie;\n\t\n\t@VisibleForTesting SecureDexClassLoader(\n            @NonNull String dexPath,\n            @NonNull DexClassLoader dexClassLoader,\n            @NonNull Context parentContext,\n            @NonNull Map<String, URL> sanitizePackageNameToCertificateMap,\n\t\t\tboolean performLazyEvaluation,\n            @NonNull ContainerSignatureVerifier containerSignatureVerifier,\n            @NonNull CertificateFactory certificateFactory) {\n\t\t\n\t\tthis.dexClassLoader = dexClassLoader;\n        this.performLazyEvaluation = performLazyEvaluation;\n        this.containerSignatureVerifier = containerSignatureVerifier;\n        this.certificateFactory = certificateFactory;\n\t\t\n\t\tcertificateFolder =\n\t\t\t\tparentContext.getDir(IMPORTED_CERTIFICATE_PRIVATE_DIRECTORY_NAME, MODE_PRIVATE);\n\t\tcontainersFolder =\n\t\t\t\tparentContext.getDir(IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME, MODE_PRIVATE);\n\n\t\tfileDownloader = new FileDownloader(parentContext);\n\t\t\n\t\thasBeenWipedOut = false;\n\t\t\n\t\tlazyAlreadyVerifiedPackageNameSet = synchronizedSet(new HashSet<String>());\n\n        packageNameToContainerPathMap =\n                synchronizedMap(generatePackageNameToContainerPathMap(dexPath));\n\n        packageNameToCertificateMap = ImmutableMap.copyOf(sanitizePackageNameToCertificateMap);\n\n        packageNameTrie = initializePackageNameTrieBasedOnContainerAndCertificateMaps(\n                packageNameToContainerPathMap, sanitizePackageNameToCertificateMap);\n\n        if (!performLazyEvaluation) {\n            // If an eager approach is chosen, all containers have to be verified now,\n            // and invalid ones must be removed.\n            performEagerEvaluationOnAllContainers();\n        }\n\t}\n\n    private static Map<String, String> generatePackageNameToContainerPathMap(\n            @NonNull String dexPath) {\n        Map<String, String> packageNameToContainerPathMap = new LinkedHashMap<>();\n\n        // Analyze each path in dexPath, find its package name and\n        // populate packageNameToContainerPathMap accordingly\n        DexPathStringProcessor dexPathStringProcessor = new DexPathStringProcessor(dexPath);\n\n        while (dexPathStringProcessor.hasNextDexPathString()) {\n            String currentPath = dexPathStringProcessor.nextDexPathString();\n\n            // In jar containers you may have classes from different package names, while in apk\n            // there is usually only one of those.\n            Optional<ImmutableSet<String>> optionalPackageNameSet =\n                    getPackageNamesFromContainerPath(currentPath);\n\n            if (optionalPackageNameSet.isPresent() && !optionalPackageNameSet.get().isEmpty()) {\n\n                for (String packageName : optionalPackageNameSet.get()) {\n\n                    // This is a valid entry so it must be added to packageNameToContainerPathMap\n                    String previousPath = packageNameToContainerPathMap.put(packageName, currentPath);\n\n                    // If previous path is not null, it means that one of the previous analyzed\n                    // path had the same package name (this is a possibility for JAR containers..)\n                    if (previousPath != null) {\n\n                        // TODO Up to now only a warning message is registered in the logs and the most\n                        // fresh of the two references is stored.\n                        Log.w(TAG_SECURE_DEX_CLASS_LOADER, \"Package Name \" + packageName + \" is not unique!\\n Previous path: \"\n                                + previousPath + \";\\n New path: \" + currentPath + \";\");\n                    }\n                }\n            }\n        }\n\n        return packageNameToContainerPathMap;\n    }\n\n    private static Optional<ImmutableSet<String>> getPackageNamesFromContainerPath(\n\t\t\t@NonNull String containerPath) {\n\n\t\t// Filter empty or missing path input\n\t\tif (containerPath.isEmpty() ||\n                !(new File(containerPath).exists())) {\n            return Optional.absent();\n        }\n\n\t\t// JAR container case (APK are simply an extension of jar files):\n\t\t// 1. Open the jar file.\n\t\t// 2. Look for the \"classes.dex\" entry inside the container.\n\t\t// 3. If it is present, retrieve package names by parsing it as a DexFile.\n\n\t\tboolean isAValidJar = false;\n\t\tJarFile containerJar = null;\n\n\t\ttry {\n\n\t\t\t// Open the jar container..\n\t\t\tcontainerJar = new JarFile(containerPath);\n\n\t\t\t// Look for the \"classes.dex\" entry inside the container.\n\t\t\tif (containerJar.getJarEntry(CLASSES_DEX_ENTRY_NAME) != null)\n\t\t\t\tisAValidJar = true;\n\n\t\t} catch (IOException e) {\n\t\t\treturn Optional.absent();\n\t\t} finally {\n\t\t\tif (containerJar != null)\n\t\t\t\ttry {\n\t\t\t\t\tcontainerJar.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t}\n\n\t\tif (isAValidJar) {\n\n\t\t\t// Use a DexFile object to parse the classes inside of the jar container and retrieve package names..\n\t\t\tDexFile dexFile;\n\n\t\t\t// Since in a jar there may be different package names for each class\n\t\t\t// but at the same time I want to keep just one record for each package\n\t\t\t// name, a set data structure fits well while processing.\n\t\t\tImmutableSet.Builder<String> packageNameSetBuilder = ImmutableSet.builder();\n\n\t\t\ttry {\n\n\t\t\t\t// Temporary file location for the loaded classes inside of the jar container\n\t\t\t\tString outputDexTempPath =\n\t\t\t\t\t\textractFilePathWithoutExtensionFromFilePath(containerPath) + ODEX_EXTENSION;\n\n\t\t\t\t// Load the dex classes inside the temporary file.\n\t\t\t\tdexFile = loadDex(containerPath, outputDexTempPath, 0);\n\n\t\t\t\tEnumeration<String> dexEntries = dexFile.entries();\n\n\t\t\t\twhile (dexEntries.hasMoreElements()) {\n\n\t\t\t\t\t// Full class name, used to extract a valid package name.\n\t\t\t\t\tString fullClassName = dexEntries.nextElement();\n\t\t\t\t\t//Log.i(TAG_SECURE_DEX_CLASS_LOADER, fullClassName);\n\n\t\t\t\t\t// Cancel white spaces before processing the full class name..\n\t\t\t\t\t// It may happen to find them while parsing class names..\n\t\t\t\t\twhile (fullClassName.startsWith(\" \"))\n\t\t\t\t\t\tfullClassName = fullClassName.substring(1, fullClassName.length());\n\n\t\t\t\t\tint lastIndexPackageName = fullClassName.lastIndexOf(\".\");\n\n\t\t\t\t\tif (lastIndexPackageName != -1) {\n\n\t\t\t\t\t\tString packageName = fullClassName.substring(0, lastIndexPackageName);\n\t\t\t\t\t\tpackageNameSetBuilder.add(packageName);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// Finally erase the .odex file since it's not necessary anymore..\n\t\t\t\tnew File(outputDexTempPath).delete();\n\n\t\t\t} catch (IOException e) {\n\t\t\t\t// Problem parsing the attached classes.dex so no valid package name\n\t\t\t\treturn Optional.absent();\n\t\t\t}\n\n\t\t\treturn Optional.of(packageNameSetBuilder.build());\n\t\t}\n\n\t\t// If classes.dex is not present in the jar, the jar container is not valid\n\t\treturn Optional.absent();\n\t}\n\n    private static PackageNameTrie initializePackageNameTrieBasedOnContainerAndCertificateMaps(\n            @NonNull Map<String, String> packageNameToContainerPathMap,\n            @NonNull Map<String, URL> packageNameToCertificateMap) {\n        PackageNameTrie packageNameTrie = new PackageNameTrie();\n\n        for (String packageNameIdentifyingAContainer : packageNameToContainerPathMap.keySet()) {\n            packageNameTrie.generateEntriesForPackageName(packageNameIdentifyingAContainer);\n        }\n\n        for (String packageNameWithACertificate : packageNameToCertificateMap.keySet()) {\n            packageNameTrie.setPackageNameHasAssociatedCertificate(packageNameWithACertificate);\n        }\n\n        return packageNameTrie;\n    }\n\n    private void performEagerEvaluationOnAllContainers() {\n        // Get the distinct set of containers path..\n        Set<String> containersToVerifySet = new HashSet<>(packageNameToContainerPathMap.values());\n\n        // Check how many containers need to be verified..\n        if (containersToVerifySet.size() < MINIMUM_NUMBER_OF_CONTAINERS_FOR_CONCURRENT_VERIFICATION) {\n\n            // Choose standard single thread verification.\n            verifyAllContainersSignature();\n        } else {\n\n            // Perform a concurrent container verification.\n            verifyAllContainersSignatureConcurrently(containersToVerifySet);\n        }\n    }\n\n\t// This method is invoked only in the case of an eager evaluation.\n\t// It will check that all the provided containers successfully\n\t// execute the signature verification step against the certificate associated\n\t// to those. Containers which fail the test will be removed.\n\tprivate void verifyAllContainersSignature() {\n\t\t\n\t\t// This map is used to check whether one container has been already verified and the\n\t\t// result of the signature verification process.\n\t\tMap<String, Boolean> alreadyCheckedContainerMap = new HashMap<>();\n\t\t\n\t\t// Analyze all the package names which are linked to a container.\n\t\tIterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator();\n\t\t\n\t\twhile (packageNamesIterator.hasNext()) {\n\t\t\t\n\t\t\tString currentPackageName = packageNamesIterator.next();\n\t\t\tString containerPath = packageNameToContainerPathMap.get(currentPackageName);\n\t\t\t\n\t\t\t// At first check whether the signature verification on this container has been already performed\n\t\t\tif (alreadyCheckedContainerMap.containsKey(containerPath)) {\n\t\t\t\t\n\t\t\t\t// In this case depending on the previous verification result\n\t\t\t\t// decide whether this package name should be removed or not\n\t\t\t\tif (!alreadyCheckedContainerMap.get(containerPath))\n\t\t\t\t\tpackageNamesIterator.remove();\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\t// A complete signature verification on the container must be performed and \n\t\t\t\t// depending on the final result the alreadyCheckedContainerMap will be updated.\n\t\t\t\t\n\t\t\t\t// At first find the package name which is closest in hierarchy to the target one\n\t\t\t\t// and has an associated URL for a certificate.\n\t\t\t\tOptional<String> optionalRootPackageNameWithCertificate =\n\t\t\t\t\t\tpackageNameTrie.getPackageNameWithAssociatedCertificate(currentPackageName);\n\t\t\t\t\n\t\t\t\tX509Certificate verifiedCertificate = null;\n\t\t\t\t\n\t\t\t\t// Check that such a package name exists and, in this case, try to import the certificate.\n\t\t\t\tif (optionalRootPackageNameWithCertificate.isPresent()) {\n\t\t\t\t\t\n\t\t\t\t\t// Try to find and import the certificate used to check the signature of .apk or .jar container\n\t\t\t\t\tverifiedCertificate = importCertificateFromPackageName(\n                            optionalRootPackageNameWithCertificate.get());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Relevant only if a verified certificate object is found.\n\t\t\t\tboolean signatureCheckIsSuccessful = true;\n\t\t\t\t\n\t\t\t\tif (verifiedCertificate != null) {\n\t\t\t\t\t\n\t\t\t\t\t// We were able to get a valid certificate either directly from the local cache directory or after having \n\t\t\t\t\t// downloaded it from the web securely.\n\t\t\t\t\t// Now it's time to check whether this certificate was used to sign the class to be loaded.\n\t\t\t\t\t\n\t\t\t\t\tsignatureCheckIsSuccessful =\n                            containerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                                    containerPath, verifiedCertificate);\n\t\t\t\t\t\n\t\t\t\t\t// Signature verification result..\n\t\t\t\t\tif (signatureCheckIsSuccessful) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// This container is valid so all of those package names which load\n\t\t\t\t\t\t// classes from it should be successful when loadClass() is called.\n\t\t\t\t\t\talreadyCheckedContainerMap.put(containerPath, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif ((verifiedCertificate == null) || ((verifiedCertificate != null) && (signatureCheckIsSuccessful == false))) {\n\t\t\t\t\t\n\t\t\t\t\t// In this case the map must be updated stating that this container has been\n\t\t\t\t\t// already checked and it fails the signature verification.\n\t\t\t\t\talreadyCheckedContainerMap.put(containerPath, false);\n\t\t\t\t\t\n\t\t\t\t\t// Then the container should be erased.\n\t\t\t\t\tFile containerToRemove = new File(containerPath);\n\t\t\t\t\tif (!containerToRemove.delete())\n\t\t\t\t\t\tLog.w(TAG_SECURE_DEX_CLASS_LOADER, \"It was impossible to delete \" + containerPath);\n\t\t\t\t\t\n\t\t\t\t\t// Finally this package name should be removed from the map of those\n\t\t\t\t\t// which are allowed to load classes.\n\t\t\t\t\tpackageNamesIterator.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\t\n\t// This method is invoked only in the case of an eager evaluation.\n\t// It will check that all the provided containers successfully\n\t// execute the signature verification step against the certificate associated\n\t// to those. Containers which fail the test will be removed.\n\tprivate void verifyAllContainersSignatureConcurrently(Set<String> containersPathToVerifySet) {\n\t\t\n\t\t\n\t\t// Initialize helper map which links a container to the certificate to validate it..\n\t\tMap<String, String> containerPathToRootPackageNameMap = new LinkedHashMap<>();\n\t\t\n\t\t// Analyze all the package names which are linked to a container.\n\t\tIterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator();\n\t\t\t\t\n\t\t// Scan all package names and find a suitable root package name \n\t\t// with an associated certificate to validate each container.\n\t\twhile (packageNamesIterator.hasNext()) {\n\t\t\t\n\t\t\tString currentPackageName = packageNamesIterator.next();\n\t\t\t\n\t\t\t// At first find the package name which is closest in hierarchy to the target one\n\t\t\t// and has an associated URL for a certificate.\n\t\t\tOptional<String> optionalRootPackageNameWithCertificate =\n                    packageNameTrie.getPackageNameWithAssociatedCertificate(currentPackageName);\n\t\t\t\n\t\t\tif (optionalRootPackageNameWithCertificate.isPresent()) {\n\t\t\t\t\n\t\t\t\t// Insert a valid entry into the container to certificate Map\n\t\t\t\tcontainerPathToRootPackageNameMap.put(packageNameToContainerPathMap.get(\n                        currentPackageName), optionalRootPackageNameWithCertificate.get());\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Initialize the set of successfully verified containers\n\t\tSet<String> successVerifiedContainerPathSet = synchronizedSet(new HashSet<String>());\n\t\t\n\t\tif (!containerPathToRootPackageNameMap.isEmpty()) {\n\t\t\t\n\t\t\t// Initialize the thread pool executor with number of thread equals to the\n\t\t\t// number of containers to verify..\n\t\t\tExecutorService threadSignatureVerificationPool = Executors.newFixedThreadPool(containerPathToRootPackageNameMap.size());\t\t\t\n\t\t\tList<Future<?>> futureTaskList = new ArrayList<>();\n\t\t\t\n\t\t\tIterator<String> containerPathIterator = containerPathToRootPackageNameMap.keySet().iterator();\n\t\t\t\n\t\t\twhile (containerPathIterator.hasNext()) {\n\t\t\t\t\n\t\t\t\tString currentContainerPath = containerPathIterator.next();\n\t\t\t\t\n\t\t\t\t// Submit a new signature verification thread on a container and store a \n\t\t\t\t// reference in the future objects list.\n\t\t\t\tFuture<?> futureTask = threadSignatureVerificationPool.submit(\n                        new SignatureVerificationTask(\n                                currentContainerPath,\n                                containerPathToRootPackageNameMap.get(currentContainerPath),\n                                successVerifiedContainerPathSet));\n\t\t\t\tfutureTaskList.add(futureTask);\n\t\t\t}\n\t\t\t\n\t\t\t// Stop accepting new tasks for the current threadSignatureVerificationPool\n\t\t\tthreadSignatureVerificationPool.shutdown();\n\t\t\t\n\t\t\tfor (Future<?> futureTask : futureTaskList) {\n\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\t\n\t\t\t\t\t// Wait till the current task for signature verification is finished..\n\t\t\t\t\tfutureTask.get();\n\t\t\t\t\t\n\t\t\t\t} catch (InterruptedException | ExecutionException e) {\n\t\t\t\t\t\n\t\t\t\t\t// Issue while executing the verification on a thread\n\t\t\t\t\tLog.w(TAG_SECURE_DEX_CLASS_LOADER, \"One of the thread failed during signature verification because of \" + e.getCause().toString());\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\ttry {\n\t\t\t\t\n\t\t\t\t// Join all the threads here..\n\t\t\t\tthreadSignatureVerificationPool.awaitTermination(MINIMUM_NUMBER_OF_CONTAINERS_FOR_CONCURRENT_VERIFICATION, KEEP_ALIVE_TIME_UNIT);\n\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\n\t\t\t\t// One or more of the thread were still busy.. This should not happen..\n\t\t\t\tLog.w(TAG_SECURE_DEX_CLASS_LOADER, \"At least one thread for signature verification was still busy and so it was interrupted\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Now all the package names are scanned again and removed from the packageNameToContainerPathMap \n\t\t// if their container is not one of the valid ones..\n\t\tIterator<String> packageNamesAfterVerificationIterator = packageNameToContainerPathMap.keySet().iterator();\n\t\t\n\t\twhile (packageNamesAfterVerificationIterator.hasNext()) {\n\t\t\t\n\t\t\tString currentPackageName = packageNamesAfterVerificationIterator.next();\n\t\t\t\n\t\t\t// Verify that at least one of the prefix of the current package name was designated \n\t\t\t// for loading its classes.\n\t\t\tOptional<String> optionalRootPackageNameAllowedForLoading =\n                    packageNameTrie.getPackageNameWithAssociatedCertificate(currentPackageName);\n\t\t\t\n\t\t\tif (!optionalRootPackageNameAllowedForLoading.isPresent() ||\n                    !successVerifiedContainerPathSet.contains(packageNameToContainerPathMap.get(currentPackageName))) {\n\t\t\t\t\n\t\t\t\t// The container linked to this package name did not succeed in the verification process.\n\t\t\t\t// No class with this package name can be loaded..\n\t\t\t\tpackageNamesAfterVerificationIterator.remove();\n\t\t\t}\n\t\t\t\t\n\t\t}\n\t\t\n\t\t// In the end all the containers that failed the verification are deleted..\n\t\tIterator<String> containersPathToVerifyIterator = containersPathToVerifySet.iterator();\n\t\t\n\t\twhile (containersPathToVerifyIterator.hasNext()) {\n\t\t\t\n\t\t\tString currentContainerPath = containersPathToVerifyIterator.next();\n\t\t\t\n\t\t\tif (!successVerifiedContainerPathSet.contains(currentContainerPath)) {\n\t\t\t\t\n\t\t\t\t// This container did not overcome successfully the signature verification\n\t\t\t\t// so it should be deleted from the cached container directory.\n\t\t\t\tif (!(new File(currentContainerPath).delete()))\n\t\t\t\t\t\tLog.w(TAG_SECURE_DEX_CLASS_LOADER, \"Issue while deleting conainer located at \" + currentContainerPath);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tclass SignatureVerificationTask implements Runnable {\n\n\t\t// Location of the container to verify.\n\t\tprivate String containerPath;\n\t\t// Package name associated with a certificate.\n\t\tprivate String rootPackageNameWithCertificate;\n\t\t// Concurrent set of containers that has been successfully verified.\n\t\tprivate final Set<String> successVerifiedContainerSet;\n\t\t\n\t\tpublic SignatureVerificationTask(String containerPath, String rootPackageNameWithCertificate, Set<String> successVerifiedContainerSet) {\n\t\t\t\n\t\t\t// Simply copy all the incoming parameters..\n\t\t\tthis.containerPath = containerPath;\n\t\t\tthis.rootPackageNameWithCertificate = rootPackageNameWithCertificate;\n\t\t\tthis.successVerifiedContainerSet = successVerifiedContainerSet;\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic void run() {\n\t\t\t\n\t\t\t// Moves the current Thread into the background\n\t        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT);\n\t\t\t\n\t\t\t// This runnable class performs a full signature verification on the \n\t\t\t// associated container\n\t\t\t\n\t\t\t// Try to find and import the certificate used to check the signature of .apk or .jar container\n\t\t\tX509Certificate verifiedCertificate = importCertificateFromPackageName(rootPackageNameWithCertificate);\n\t\t\t\n\t\t\tif (verifiedCertificate != null) {\n\t\t\t\t\n\t\t\t\t// We were able to get a valid certificate either directly from the local cache directory or after having \n\t\t\t\t// downloaded it from the web securely.\n\t\t\t\t// Now it's time to check whether this certificate was used to sign the class to be loaded.\n\t\t\t\t\n\t\t\t\tboolean signatureCheckIsSuccessful =\n                        containerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                                containerPath, verifiedCertificate);\n\t\t\t\t\n\t\t\t\t// Signature verification result..\n\t\t\t\tif (signatureCheckIsSuccessful) {\n\t\t\t\t\n\t\t\t\t\t// If the signature verification on the container succeeds, insert this container path\n\t\t\t\t\t// into the set of those containers which successfully pass the signature verification process.\n\t\t\t\t\tsynchronized (successVerifiedContainerSet) {\n\t\t\t\t\t\n\t\t\t\t\t\tsuccessVerifiedContainerSet.add(containerPath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t}\n\n\t/* (non-Javadoc)\n\t * @see java.lang.ClassLoader#loadClass(java.lang.String)\n\t */\n\t/**\n\t * @see java.lang.ClassLoader#loadClass(java.lang.String)\n\t * @param className\n\t *  the full class name to load. It must fit the form \"package name + . + class name\".\n\t *  A valid full class name is for example \"it.polimi.myapplication.classA\"; while \"classA\" is not\n\t *  enough since it misses the package name and so {@link SecureDexClassLoader} will not find any\n\t *  class to load.\n\t * @return\n\t *  Either a class that needs to be casted at runtime accordingly to className if the verification\n\t *  process succeeds or a {@code null} pointer in case that at least one of the security\n\t *  constraints for secure dynamic class loading is violated.\n\t * @throws ClassNotFoundException\n\t *  this exception is raised whenever no security constraint is violated but still the target class is\n\t *  not found in any of the available containers used to instantiate this {@link SecureDexClassLoader} object.\n\t */\n\tpublic Class<?> loadClass(@NonNull String className) throws ClassNotFoundException {\n\t\t\n        checkNotNull(className, \"The name of the class to load must be not null\");\n        checkArgument(!className.isEmpty(), \"The name of the class to load must be not empty\");\n\n\t\t// Cached data have been wiped out so some of the required\n\t\t// resources may have been erased..\n\t\tif (hasBeenWipedOut) return null;\n\t\t\n\t\t// At first the name of the certificate possibly stored \n\t\t// in the application private directory is generated\n\t\t// from the package name.\n\t\tString packageName = className.substring(0, className.lastIndexOf('.'));\n\t\t\n\t\t// Retrieve the path of the container from package name.\n\t\t// If there is not such a path, then no class can be loaded.\n\t\tString containerPath;\n\t\t\n\t\tsynchronized (packageNameToContainerPathMap) {\n\t\t\t\n\t\t\tcontainerPath = packageNameToContainerPathMap.get(packageName);\n\t\t}\n\t\t\n\t\tif (containerPath == null) return null;\n\t\t\n\t\tif (performLazyEvaluation) {\n\t\t\t\n\t\t\t// If SecureDexClassLoader is running in LAZY mode, now it is time \n\t\t\t// to verify the signature of the container associated with the class to load..\n\t\t\tboolean alreadyVerifiedPackageName;\n\t\t\t\n\t\t\t// Force synchronization on this check on the set..\n\t\t\tsynchronized (lazyAlreadyVerifiedPackageNameSet) {\n\t\t\t\n\t\t\t\t// At first check whether this package name has been already verified..\n\t\t\t\talreadyVerifiedPackageName = lazyAlreadyVerifiedPackageNameSet.contains(packageName);\n\t\t\t}\n\t\t\t\n\t\t\tif (alreadyVerifiedPackageName) {\n\n\t\t\t\t// Even if the container associated to this package name may have been verified correctly,\n\t\t\t\t// it is necessary to verify that the user also wants to dynamically load classes from this package name.\n\t\t\t\tOptional<String> optionalRootPackageNameWithCertificate =\n                        packageNameTrie.getPackageNameWithAssociatedCertificate(packageName);\n\t\t\t\t\n\t\t\t\tif (optionalRootPackageNameWithCertificate.isPresent()) {\n\t\t\t\t\t\n\t\t\t\t\t// The container associated to this package name has been already verified once so classes\n\t\t\t\t\t// belonging to this package name can be immediately loaded.\n\t\t\t\t\treturn dexClassLoader.loadClass(className);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t\n\t\t\t\t// This branch represents those classes, whose package name and related container has not been analyzed yet..\n\t\t\t\t\n\t\t\t\t// At first find the package name which is closest in hierarchy to the target one\n\t\t\t\t// and has an associated URL for a certificate.\n\t\t\t\tOptional<String> optionalRootPackageNameWithCertificate =\n                        packageNameTrie.getPackageNameWithAssociatedCertificate(packageName);\n\t\t\t\t\n\t\t\t\tX509Certificate verifiedCertificate = null;\n\t\t\t\t\n\t\t\t\t// Check that such a package name exists and, in this case, try to import the certificate.\n\t\t\t\tif (optionalRootPackageNameWithCertificate.isPresent()) {\n\t\t\t\t\t\n\t\t\t\t\t// Try to find and import the certificate used to check the signature of .apk or .jar container\n\t\t\t\t\tverifiedCertificate = importCertificateFromPackageName(\n                            optionalRootPackageNameWithCertificate.get());\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (verifiedCertificate != null) {\n\t\t\t\t\t\n\t\t\t\t\t// We were able to get a valid certificate either directly from the local cache directory or after having \n\t\t\t\t\t// downloaded it from the web securely.\n\t\t\t\t\t// Now it's time to check whether this certificate was used to sign the class to be loaded.\n\t\t\t\t\t\n\t\t\t\t\tboolean signatureCheckIsSuccessful =\n                            containerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                                    containerPath, verifiedCertificate);\n\t\t\t\t\t\n\t\t\t\t\t// Signature verification result..\n\t\t\t\t\tif (signatureCheckIsSuccessful) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// The signature of the related .apk or .jar container\n\t\t\t\t\t\t// was successfully verified against the valid certificate.\n\t\t\t\t\t\t// Integrity was granted and the class can be loaded.\n\t\t\t\t\t\t// Before doing so this package name is stored into the set of \n\t\t\t\t\t\t// those which have been already successfully verified.\n\t\t\t\t\t\t// lazyAlreadyVerifiedPackageNameSet.add(packageName);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Look for all the package names linked to the same container.\n\t\t\t\t\t\t// Since the verification steps is performed on the whole container, \n\t\t\t\t\t\t// all the package names linked to it will automatically succeed on the\n\t\t\t\t\t\t// signature verification and so they need to be stored into the set of \n\t\t\t\t\t\t// those package names which have been already successfully verified..\n\t\t\t\t\t\t\n\t\t\t\t\t\tsynchronized (lazyAlreadyVerifiedPackageNameSet) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tIterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\twhile (packageNamesIterator.hasNext()) {\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tString currentPackageName = packageNamesIterator.next();\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) {\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t// This collection won't be modified if it already contains the current analyzed package name..\n\t\t\t\t\t\t\t\t\tlazyAlreadyVerifiedPackageNameSet.add(currentPackageName);\n\t\t\t\t\t\t\t\t}\t\t\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn dexClassLoader.loadClass(className);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// The signature of the .apk or .jar container was not valid when compared against the selected certificate.\n\t\t\t\t\t// No class loading should be allowed and the container should be removed as well.\n\t\t\t\t\tFile containerToRemove = new File(containerPath);\n\t\t\t\t\tif (!containerToRemove.delete())\n\t\t\t\t\t\tLog.i(TAG_SECURE_DEX_CLASS_LOADER, \"It was impossible to delete \" + containerPath);\n\t\t\t\t\t\n\t\t\t\t\t// packageNameToContainerPathMap.remove(packageName);\n\t\t\t\t\t// Remove from the associative map all of those package names which are linked to this container.\n\t\t\t\t\t// In fact since this container fails the verification steps, all the classes inside of it won't be loaded.\n\t\t\t\t\tsynchronized (packageNameToContainerPathMap) {\n\t\t\t\t\t\n\t\t\t\t\t\tIterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator();\n\t\t\t\t\t\t\n\t\t\t\t\t\twhile (packageNamesIterator.hasNext()) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tString currentPackageName = packageNamesIterator.next();\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath))\n\t\t\t\t\t\t\t\tpackageNamesIterator.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Either download procedure fails and the required certificate has not been cached locally or\n\t\t\t\t// a package name with no certificate associated to its hierarchy was provided.\n\t\t\t\t// No class should be loaded since its signature can't be verified..\n\t\t\t\t// But on the other hand this do not imply that the container is necessarily malicious.\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t}\n\n\t\t// If SecureDexClassLoader is running in EAGER mode, all the required checks\n\t\t// on the containers signatures have been already performed so we can simply \n\t\t// invoke the super method loadClass() of DexClassLoader.\n\t\treturn dexClassLoader.loadClass(className);\n\t}\n\n\t// Given a package name, at first try to locate the associated certificate from the cached\n\t// certificate directory. If this check fails, try to download and store on the device the \n\t// certificate provided by the developer. If one of the two ways is successful return \n\t// a certificate instance.\n\tprivate X509Certificate importCertificateFromPackageName(String packageName) {\n\t\t\n\t\t//Trace.beginSection(\"Import Certificate\");\n\t\t// Log.i(\"Profile\",\"[Start]\tImport Certificate: \" + System.currentTimeMillis() + \" ms.\");\t\t\n\t\t\n\t\t// At first check if the correct certificate has been \n\t\t// already imported in the application-private certificate directory.\n\t\tX509Certificate verifiedCertificate = importCertificateFromAppPrivateDir(packageName);\n\t\t\t\t\n\t\tif (verifiedCertificate == null) {\n\t\t\t\t\t\n\t\t\t// No matching certificate or an expired one was found \n\t\t\t// locally and so it's necessary to download the \n\t\t\t// certificate through an Https request.\n\t\t\t//Trace.beginSection(\"Download Certificate\");\n\t\t\t// Log.i(\"Profile\",\"[Start]\tDownload Certificate: \" + System.currentTimeMillis() + \" ms.\");\n\t\t\tboolean isCertificateDownloadSuccessful = downloadCertificateRemotelyViaHttps(packageName);\n\t\t\t// Log.i(\"Profile\",\"[End]\tDownload Certificate: \" + System.currentTimeMillis() + \" ms.\");\n\t\t\t//Trace.endSection(); // end of \"Download Certificate\" section\n\t\t\t\t\t\n\t\t\tif (isCertificateDownloadSuccessful) {\n\t\t\t\t\t\t\n\t\t\t\t// Download procedure works fine and the new \n\t\t\t\t// certificate should now be in the local folder.\n\t\t\t\t// So let's try to retrieve it once again..\n\t\t\t\tverifiedCertificate = importCertificateFromAppPrivateDir(packageName);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Log.i(\"Profile\",\"[End]\tImport Certificate: \" + System.currentTimeMillis() + \" ms.\");\n\t\t//Trace.endSection(); // end of \"Import Certificate\" section\n\t\t\n\t\treturn verifiedCertificate;\n\t}\n\n\tprivate X509Certificate importCertificateFromAppPrivateDir(String packageName) {\n\t\t\n\t\t// The procedure looks for the correct certificate and \n\t\t// if a match is found, it will import it and return it.\n\t\tFile[] certMatchingFiles = certificateFolder.listFiles(new CertificateFileFilterByNameMatch(packageName));\n\t\t\n\t\tX509Certificate verifiedCertificate = null;\n\t\t\t\t\n\t\tif (certMatchingFiles != null && certMatchingFiles.length != 0) {\n\t\t\t\t\t\n\t\t\t// Import the first (and only) matching certificate from file..\n\t\t\tInputStream inStream = null;\n\t\t\t\n\t\t\ttry {\n\t\t\t\t\t\n\t\t\t\t// Since certificate files has unique package names as their own\n\t\t\t\t// name, either no one or exactly one matching certificate file will\n\t\t\t\t// be found.\n\t\t\t\tinStream = new FileInputStream(certMatchingFiles[0]);\n\t\t\t    //CertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n\t\t\t    verifiedCertificate = (X509Certificate) certificateFactory.generateCertificate(inStream);\n\t\t\t\t\t    \n\t\t\t} catch (FileNotFoundException | CertificateException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t} finally {\n\t\t\t     if (inStream != null) {\n\t\t\t         try {\n\t\t\t\t\t\tinStream.close();\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t}\n\t\t\t     }\n\t\t\t}\n\t\t\t\t\t\n\t\t\t// If the certificate was correctly created, check \n\t\t\t// if it's currently valid\n\t\t\tif (verifiedCertificate != null) {\n\t\t\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\tverifiedCertificate.checkValidity();\n\t\t\t\t\t\n\t\t\t\t\t// Evaluate whether the certificate can be used for signature verification\n\t\t\t\t\t// keyCertSignIndex is a magic number from ASN.1 definition of Key Usage.\n\t\t\t\t\tif (verifiedCertificate.getKeyUsage() != null) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tint keyCertSignIndex = 5;\n\t\t\t\t\t\tif(verifiedCertificate.getKeyUsage()[keyCertSignIndex])\n\t\t\t\t\t\t\tthrow new CertificateExpiredException(\"This certificate should not be used for code verification!\");\n\t\t\t\t\t\t\n\t\t\t\t\t\tLog.d(TAG_SECURE_DEX_CLASS_LOADER, Arrays.toString(verifiedCertificate.getKeyUsage()));\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t// Check whether the certificate used to verify is the one \n\t\t\t\t\t// used by Android in Debug Mode. If so, discard this certificate\n\t\t\t\t\t// since it's not secure.\n\t\t\t\t\tString androidDebugModeDN = \"C=US,O=Android,CN=Android Debug\";\n\t\t\t\t\tX500Principal androidDebugModePrincipal = new X500Principal(androidDebugModeDN);\n\t\t\t\t\tif (\tverifiedCertificate.getIssuerX500Principal().equals(androidDebugModePrincipal) ||\n\t\t\t\t\t\t\tverifiedCertificate.getSubjectX500Principal().equals(androidDebugModePrincipal)\t)\n\t\t\t\t\t\tthrow new CertificateExpiredException(\"Android Debug Certificate can't be accepted to sign containers!\");\n\t\t\t\t\t\n\t\t\t\t} catch (CertificateExpiredException\n\t\t\t\t\t\t| CertificateNotYetValidException e) {\n\t\t\t\t\t// This certificate is not valid!\n\t\t\t\t\t// Discard it and erase the copy of \n\t\t\t\t\t// the file on the device memory\n\t\t\t\t\tverifiedCertificate = null;\n\t\t\t\t\tString certFileToErase = certMatchingFiles[0].getName();\n\t\t\t\t\tif (certMatchingFiles[0].delete()) {\n\t\t\t\t\t\tLog.i(TAG_SECURE_DEX_CLASS_LOADER, \"Expired certificate \" + certFileToErase + \" has been erased.\");\n\t\t\t\t\t}else {\n\t\t\t\t\t\tLog.w(TAG_SECURE_DEX_CLASS_LOADER, \"Problems while deleting expired certificate \" + certFileToErase + \"!\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\t\n\t\t}\n\t\t\n\t\t// At the end return the result of the procedure: \n\t\t// either null or a valid and not expired certificate\n\t\treturn verifiedCertificate;\n\t}\n\n\tprivate boolean downloadCertificateRemotelyViaHttps(String packageName) {\n\t\t\n\t\t// Find remote URL of the certificate from the related map through class package name.\n\t\t// All URLs here use method HTTPS.\n\t\tURL certificateRemoteURL = packageNameToCertificateMap.get(packageName);\n\t\t\n\t\t// The new certificate should be stored in the application private directory\n\t\t// and its name should be the same as the package name.\n\t\tString localCertPath =\n                certificateFolder.getAbsolutePath() + File.separator + packageName + PEM_EXTENSION;\n\t\t\n\t\t// Return the result of the download procedure (redirect here is not permitted).\n\t\treturn fileDownloader.downloadRemoteResource(certificateRemoteURL, localCertPath, false);\n\t}\n\t\n\t/**\n\t * Sometimes it may be useful to remove those data that have been cached in \n\t * the private application folders (basically for performance reason or for making \n\t * {@link SecureDexClassLoader} works also partially offline). A call to this method solves the issue.\n\t * <p>\n\t * Please notice that a call to this method with both the parameters set to false \n\t * has no effect.\n\t * <p>\n\t * In any of the other cases the content of the related folder(s) will be erased and \n\t * since some of the data may have been used by {@link SecureDexClassLoader} instances, it is \n\t * required to the caller to create a new {@link SecureDexClassLoader} object through \n\t * {@link SecureLoaderFactory} since the already present object is going to be disabled \n\t * from loading classes dynamically.\n\t * \n\t * @param containerPrivateFolder\n\t * if the private folder where jar and apk containers downloaded from remote URL or imported from local storage needs to be wiped out.\n\t * @param certificatePrivateFolder\n\t * if the private folder containing certificates needs to be wiped out.\n\t */\n\tpublic void wipeOutPrivateAppCachedData(boolean containerPrivateFolder, boolean certificatePrivateFolder) {\n\t\t\n\t\t// This is a useless call.. Nothing will happen..\n\t\tif (!containerPrivateFolder && !certificatePrivateFolder) return;\n\t\t\n\t\tList<File> fileToEraseList = new ArrayList<>();\n\t\t\n\t\tif (containerPrivateFolder) {\n\t\t\t\n\t\t\t// It is required to erase all the files in the application\n\t\t\t// private container folder..\n\t\t\tFile[] containerFiles = containersFolder.listFiles();\n\n            Collections.addAll(fileToEraseList, containerFiles);\n\t\t}\n\t\t\n\t\tif (certificatePrivateFolder) {\n\t\t\t\n\t\t\t// It is required to erase all the files in the application\n\t\t\t// private certificate folder..\n\t\t\tFile[] certificateFiles = certificateFolder.listFiles();\n\n            Collections.addAll(fileToEraseList, certificateFiles);\n\t\t}\n\n        for (File file : fileToEraseList) {\n\n            // Check whether the selected resource is a container (jar or apk)\n            // or a certificate (pem)\n            Optional<String> optionalExtension = extractExtensionFromFilePath(file.getAbsolutePath());\n\n            if (optionalExtension.isPresent() &&\n                    (optionalExtension.get().equals(APK_EXTENSION) ||\n                            optionalExtension.get().equals(JAR_EXTENSION) ||\n                            optionalExtension.get().equals(PEM_EXTENSION))) {\n\n                if (file.delete())\n                    Log.i(TAG_SECURE_DEX_CLASS_LOADER, file.getPath() + \" has been erased.\");\n                else\n                    Log.i(TAG_SECURE_DEX_CLASS_LOADER, file.getPath() + \" was NOT erased.\");\n            }\n        }\n\t\t\n\t\thasBeenWipedOut = true;\n\t}\n}\n"
  },
  {
    "path": "gnr/app/src/main/java/it/necst/grabnrun/SecureLoaderFactory.java",
    "content": "/*******************************************************************************\n * Copyright 2014 Luca Falsina\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage it.necst.grabnrun;\n\nimport static android.content.Context.MODE_PRIVATE;\nimport static com.google.common.base.Preconditions.checkArgument;\nimport static com.google.common.base.Preconditions.checkNotNull;\nimport static it.necst.grabnrun.FileHelper.endsWithJarOrApkExtension;\nimport static it.necst.grabnrun.FileHelper.extractExtensionFromFilePath;\nimport static it.necst.grabnrun.FileHelper.extractFileNameFromFilePath;\nimport static it.necst.grabnrun.PackageNameHelper.isAValidPackageName;\nimport static it.necst.grabnrun.PackageNameHelper.revertPackageNameToURL;\n\nimport android.content.Context;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.util.Base64;\nimport android.util.Log;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport com.google.common.base.Optional;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport dalvik.system.DexClassLoader;\n\n/**\n * A factory class that generates instances of classes used to\n * retrieve containers holding code to execute dynamically in a secure way.\n * \n * @author Luca Falsina\n */\npublic class SecureLoaderFactory {\n\n    // Unique identifier used for Log entries\n\tprivate static final String TAG_SECURE_FACTORY = SecureLoaderFactory.class.getSimpleName();\n\n    // Name of the folder user to store imported containers (both coming from remote, or local resources)\n    static final String IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME = \"imported_cont\";\n    @VisibleForTesting static final String OUTPUT_DEX_CLASSES_DIRECTORY_NAME = \"dex_classes\";\n    @VisibleForTesting static final String X_509_CERTIFICATE = \"X.509\";\n\n    /**\n     * When a URL for a remote container is found, this field specifies the default time interval,\n     * expressed in days, before a local copy of it, stored in an application-private directory,\n     * will be considered rotten, and so not acceptable to be cached.\n     * <p>\n     * Those local copies of remote containers, whose life time is greater than this\n     * field value, will be erased from the device storage in stead of being cached.\n     * <p>\n     * You can change this duration by generating the {@link SecureLoaderFactory} instance\n     * with {@link SecureLoaderFactory#SecureLoaderFactory(android.content.Context, int)}.\n     */\n    public static final int DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRATION = 5;\n    private static final String HTTP_PROTOCOL_STRING = \"http\";\n    private static final String HTTPS_PROTOCOL_STRING = \"https\";\n\n\n    private Context context;\n\n\t// Used to compute the digest of different containers\n\t// in order to check which has been already cached.\n\tprivate MessageDigest messageDigest;\n\n\tprivate int daysBeforeContainerCacheExpiration;\n\n\t/**\n\t * Creates a {@link SecureLoaderFactory} used to check and generate instances \n\t * from secure dynamic code loader classes.\n\t * <p>\n\t * It requires a {@link android.content.Context} (i.e. the launching activity) which\n\t * should be used to manage and retrieve internal directories \n\t * of the application.\n     * <p>\n     * The number of days for which a local copy of a remote resource is considered acceptable\n     * is set to the value of {@link SecureLoaderFactory#DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRATION}.\n\t * \n\t * @param parentContext\n\t *  The content wrapper coming from the launching Activity.\n\t */\n\tpublic SecureLoaderFactory(Context parentContext) {\n\t\n\t\tthis(parentContext, DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRATION);\n\t}\n\t\n\t/**\n     * Creates a {@link SecureLoaderFactory} used to check and generate instances\n     * from secure dynamic code loader classes.\n     * <p>\n     * It requires a {@link android.content.ContextWrapper} (i.e. the launching activity) which\n     * should be used to manage and retrieve internal directories\n     * of the application.\n     * <p>\n\t * It allows to decide the time interval in days before which a local copy of a remote\n     * container, will be considered fresh and so acceptable to be cached.\n\t * <p>\n\t * If a negative value is provided for the second parameter, {@link SecureLoaderFactory} will\n\t * throws an {@link IllegalArgumentException}.\n\t * \n\t * @param parentContext\n\t *  The content wrapper coming from the launching Activity.\n\t * @param daysBeforeContainerCacheExpiration\n\t *  The value in days for which a local copy of a remote container is considered fresh,\n     *  thus acceptable to be cached.\n     * @throws IllegalArgumentException if the number of days is not greater than zero\n\t */\n\tpublic SecureLoaderFactory(Context parentContext, int daysBeforeContainerCacheExpiration) {\n\n        checkArgument(\n                daysBeforeContainerCacheExpiration > 0,\n                \"The number of days before considering a container rotten must be greater than zero\");\n        this.daysBeforeContainerCacheExpiration = daysBeforeContainerCacheExpiration;\n\n\t\tcontext = parentContext;\n\n\t\ttry {\n\t\t\tmessageDigest = MessageDigest.getInstance(\"SHA-1\");\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tLog.e(TAG_SECURE_FACTORY, \"Wrong algorithm choice for message digest!\");\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\t\n\t/**\n\t * Creates a {@link SecureDexClassLoader} that finds interpreted and native code in a set of\n\t * provided locations (either local, or remote via HTTP, or HTTPS protocol) in dexPath.\n\t * Interpreted classes are found in a set of DEX files contained in Jar or Apk files and \n\t * stored into an application-private directory.\n\t * <p>\n\t * Before executing one of these classes the signature of the target class is \n\t * verified against the certificate associated with its package name.\n\t * Certificates location are provided by filling appropriately packageNameToCertificateMap;\n\t * each package name must be linked with the remote location of the certificate that\n\t * should be used to validate all the classes of that package. It's important \n\t * that each one of these locations uses HTTPS as its protocol; otherwise this \n\t * choice will be enforced!\n\t * If a class package name do not match any of the provided entries in the map, \n\t * certificate location will be constructed by simply reverting package name and \n\t * transforming it into a web-based URL using HTTPS.\n\t * <p>\n\t * Note that this method returns {@code null} if no matching Jar, or Apk file is found at the\n\t * provided dexPath parameter; otherwise a {@link SecureDexClassLoader} instance is returned.\n\t * <p>\n\t * Dynamic class loading with the returned {@link SecureDexClassLoader} will fail whether\n\t * at least one of these conditions is not accomplished: target class is not found in dexPath\n\t * or is in a missing remote container (i.e. Internet connectivity is not present), missing or\n\t * invalid (i.e. expired) certificate is associated with the package name of the target class,\n\t * target class signature check fails against the associated certificate.\n\t * \n\t * @param dexPath\n\t *  the list of jar/apk files containing classes and resources; these paths could\n\t *  be either local URLs pointing to a location in the device or URLs that links\n\t *  to a resource stored in the web via HTTP/HTTPS. In the latter case, if Internet\n\t *  connectivity is available, the resource will be imported in a private-application \n\t *  directory before being used.\n\t * @param libraryPath\n\t *  the list of directories containing native libraries; it may be {@code null}.\n\t * @param parent\n\t *  the parent class loader.\n\t * @param packageNameToCertificateMap\n\t *  a map that couples each package name to a URL which contains the certificate\n\t *  that must be used to validate all the classes that belong to that package\n\t *  before launching them at run time. Please notice that any URL in this map \n\t *  using HTTP protocol will be enforced to use HTTPS in stead.\n\t * @return\n\t *  a {@link SecureDexClassLoader} object which can be used to load dynamic code securely and \n\t *  uses a Eager strategy for container signature verification.\n\t */\n\tpublic SecureDexClassLoader createDexClassLoader(\n            @NonNull String dexPath,\n\t\t\t@Nullable String libraryPath,\n            @NonNull ClassLoader parent,\n            @NonNull Map<String, URL> packageNameToCertificateMap) {\n\t\t\n\t\t// The default behavior is EAGER evaluation.\n\t\t// In order to change it, simply modify the last boolean parameter from \"false\" to \"true\".\n\t\treturn createDexClassLoader(dexPath, libraryPath, parent, packageNameToCertificateMap, false);\n\t}\n\t\n\t/**\n\t * This method returns a {@link SecureDexClassLoader} instance in the same way as it is \n\t * explained in the {@link SecureLoaderFactory#createDexClassLoader(String, String, ClassLoader, Map)}. In addition it is \n\t * possible to specify the mode in which the signature verification process will be carried.\n\t * <p>\n\t * In particular by setting the performLazyEvaluation parameter on true, \n\t * a lazy verification process will be chosen. This means that the signature\n\t * of the single container associated with the target class will be evaluated \n\t * only when {@link SecureDexClassLoader#loadClass(String)} will be invoked . \n\t * If this check succeeds the target class will be loaded.\n\t * <p>\n\t * On the other hand, by setting the parameter performLazyEvaluation to false \n\t * an eager evaluation will be carried out. This means that before returning this \n\t * object, the signature verification procedure will be carried out on all the \n\t * provided containers in a CONCURRENT way and all of those that do not succeed in the process will\n\t * be blocked from loading their classes in the following loadClass() method calls.\n\t * <p>\n\t * The use of one mode in stead of the other is merely a performance-related choice.\n\t * If you do not care that much about it, just invoke\n\t * {@link SecureLoaderFactory#createDexClassLoader(String, String, ClassLoader, Map)} \n\t * which does not require to provide the performLazyEvaluation\n\t * parameter and it will perform an Eager evaluation. Otherwise as a general guideline, \n\t * prefer the lazy mode whenever you think that you won't need to load classes \n\t * from all the provided containers.\n\t * \n\t * @param dexPath\n\t *  the list of jar/apk files containing classes and resources; these paths could\n\t *  be either local URLs pointing to a location in the device or URLs that links\n\t *  to a resource stored in the web via HTTP/HTTPS. In the latter case, if Internet\n\t *  connectivity is available, the resource will be imported in a private-application \n\t *  directory before being used.\n\t * @param libraryPath\n\t *  the list of directories containing native libraries; it may be {@code null}.\n\t * @param parent\n\t *  the parent class loader.\n\t * @param packageNameToCertificateMap\n\t *  a map that couples each package name to a URL which contains the certificate\n\t *  that must be used to validate all the classes that belong to that package\n\t *  before launching them at run time. Please notice that any URL in this map \n\t *  using HTTP protocol will be enforced to use HTTPS in stead.\n\t * @param performLazyEvaluation\n\t *  the mode in which the verification will be handled. True for lazy verification;\n\t *  false for the eager one.\n\t * @return\n\t * \ta {@link SecureDexClassLoader} object which can be used to load dynamic code securely and \n\t *  uses a either Lazy, or an Eager strategy for container signature verification depending\n\t *  on the last parameter provided to this constructor.\n\t */\n\tpublic SecureDexClassLoader createDexClassLoader(\n            @NonNull String dexPath,\n            @Nullable String libraryPath,\n            @NonNull ClassLoader parent,\n            @NonNull Map<String, URL> packageNameToCertificateMap,\n\t\t\tboolean performLazyEvaluation) {\n\n        checkPreconditionsOnArgumentsForSecureDexClassLoaderCreation(\n                dexPath, parent, packageNameToCertificateMap);\n\n        // New container resources will be imported, or cached into this application private folder.\n        File importedContainerDirectory = context.getDir(\n                IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME, MODE_PRIVATE);\n        Log.d(TAG_SECURE_FACTORY, \"Download Resource Dir has been mounted at: \" +\n                importedContainerDirectory.getAbsolutePath());\n\n        // Now the location of the final loaded classes is created.\n        // Since it is assumed that the developer do not care where\n        // exactly the dex classes will be stored, an application-private,\n        // writable directory is created ad hoc.\n        File dexOutputDirectory = context.getDir(OUTPUT_DEX_CLASSES_DIRECTORY_NAME, MODE_PRIVATE);\n        Log.d(TAG_SECURE_FACTORY, \"Dex Output Dir has been mounted at: \" +\n                dexOutputDirectory.getAbsolutePath());\n\n        String finalDexPathString =\n                processDexPathStringAndImportContainers(dexPath, importedContainerDirectory);\n\n\t\t// Til now libraryPath is left untouched..\n\n        // Initialize the certificate factory\n        CertificateFactory certificateFactory = null;\n        try {\n            certificateFactory = CertificateFactory.getInstance(X_509_CERTIFICATE);\n        } catch (CertificateException e) {\n            e.printStackTrace();\n        }\n\n\t\treturn new SecureDexClassLoader(\n                finalDexPathString,\n                new DexClassLoader(\n                        finalDexPathString,\n                        dexOutputDirectory.getAbsolutePath(),\n                        libraryPath,\n                        parent),\n                context,\n                processPackageNameToCertificateMap(packageNameToCertificateMap),\n\t\t\t\tperformLazyEvaluation,\n                new ContainerSignatureVerifier(\n                        context.getPackageManager(), certificateFactory),\n                certificateFactory);\n\t}\n\n    private static void checkPreconditionsOnArgumentsForSecureDexClassLoaderCreation(\n            @NonNull String dexPath,\n            @NonNull ClassLoader parent,\n            @NonNull Map<String, URL> packageNameToCertificateMap) {\n        checkNotNull(dexPath, \"The dex path string of the containers used as source must not be null\");\n        checkArgument(\n                !dexPath.isEmpty(),\n                \"The dex path string of the containers used as source must not be empty\");\n\n        checkNotNull(parent, \"The parent class loader must not be null\");\n\n        checkNotNull(\n                packageNameToCertificateMap,\n                \"The map associating package names with the URL of the certificate for \" +\n                        \"signature check must not be null\");\n        checkArgument(\n                !packageNameToCertificateMap.isEmpty(),\n                \"The map associating package names with the URL of the certificate for \" +\n                        \"signature check must have at least one package name as key\");\n    }\n\n    private String processDexPathStringAndImportContainers(String dexPath, File importedContainerDir) {\n        StringBuilder finalDexPathStringBuilder = new StringBuilder();\n\n        CacheLogger remoteContainersCacheLogger = new CacheLogger(\n                importedContainerDir.getAbsolutePath(), daysBeforeContainerCacheExpiration);\n\n        DexPathStringProcessor dexPathStringProcessor = new DexPathStringProcessor(dexPath);\n\n        while (dexPathStringProcessor.hasNextDexPathString()) {\n\n            String singleDexPath = dexPathStringProcessor.nextDexPathString();\n            Optional<String> optionalSuccessfullyProcessedSingleDexPath;\n\n\t\t\tif (isARemoteHttpOrHttpsResource(singleDexPath)) {\n\n                optionalSuccessfullyProcessedSingleDexPath = processPathPointingToARemoteContainer(\n                        singleDexPath, importedContainerDir, remoteContainersCacheLogger);\n            }\n\t\t\telse {\n\n                optionalSuccessfullyProcessedSingleDexPath = processPathPointingToALocallyStoredContainer(\n                        singleDexPath, importedContainerDir);\n            }\n\n            if (optionalSuccessfullyProcessedSingleDexPath.isPresent()) {\n\n                finalDexPathStringBuilder\n                        .append(optionalSuccessfullyProcessedSingleDexPath.get())\n                        .append(File.pathSeparator);\n                Log.d(TAG_SECURE_FACTORY, \"Dex Path has been modified into: \" + finalDexPathStringBuilder);\n            }\n\t\t}\n\n        // Remove the last unnecessary separator from finalDexPathStringBuilder\n        // (if finalDexPathStringBuilder has at least one path inside)\n        if (finalDexPathStringBuilder.lastIndexOf(File.pathSeparator) != -1)\n            finalDexPathStringBuilder.deleteCharAt(finalDexPathStringBuilder.lastIndexOf(File.pathSeparator));\n\n        // Finalize the CacheLogger object and update the helper file on the device\n        remoteContainersCacheLogger.finalizeLog();\n\n        return finalDexPathStringBuilder.toString();\n    }\n\n    private static boolean isARemoteHttpOrHttpsResource(String resourcePath) {\n        return resourcePath.startsWith(HTTP_PROTOCOL_STRING) ||\n                resourcePath.startsWith(HTTPS_PROTOCOL_STRING);\n    }\n\n    private Optional<String> processPathPointingToARemoteContainer(\n            String singleDexPath,\n            File importedContainerDir,\n            CacheLogger remoteContainersCacheLogger) {\n        try {\n            URL currentSingleDexPathAsURL = new URL(singleDexPath);\n\n            Optional<String> cachedContainerFileName =\n                    remoteContainersCacheLogger.checkForCachedEntry(currentSingleDexPathAsURL);\n\n            if (cachedContainerFileName.isPresent()) {\n\n                // A fresh enough cached copy of the remote container is present\n                // on the device storage, so this copy can be used in stead of downloading\n                // the remote container again.\n                return Optional.of(importedContainerDir.getAbsolutePath() + File.separator +\n                        cachedContainerFileName.get());\n            } else {\n\n                // No cached copy so it is necessary to download the remote resource from the web.\n\n                //Trace.beginSection(\"Download Container\");\n                // Log.i(\"Profile\",\"[Start]\tDownload Container: \" + System.currentTimeMillis() + \" ms.\");\n                String downloadedContainerPath = downloadContainerIntoFolder(singleDexPath, importedContainerDir);\n                // Log.i(\"Profile\",\"[End]\tDownload Container: \" + System.currentTimeMillis() + \" ms.\");\n                //Trace.endSection(); // end of \"Download Container\" section\n\n                if (downloadedContainerPath != null) {\n\n                    // In such a case the download was successful.\n                    // Now the downloaded container is renamed according to its finger print.\n                    String containerDigest = computeDigestFromFilePath(downloadedContainerPath);\n\n                    File downloadedContainer = new File(downloadedContainerPath);\n\n                    if (containerDigest == null) {\n\n                        // Fingerprint computation fails. Delete the resource container and do not add\n                        // this file to the singleDexPath\n                        if (!downloadedContainer.delete())\n                            Log.w(TAG_SECURE_FACTORY, \"Issue while deleting \" + downloadedContainerPath);\n                    } else {\n\n                        // Compute the extension of the file.\n                        String extension = extractExtensionFromFilePath(downloadedContainerPath).get();\n\n                        // Rename the previous container file according to the containerDigest and its extension.\n                        String downloadedContainerFinalPath = importedContainerDir.getAbsolutePath() + File.separator + containerDigest + extension;\n\n                        File downloadContainerFinalPosition = new File(downloadedContainerFinalPath);\n\n                        if (downloadContainerFinalPosition.exists())\n                            if (!downloadContainerFinalPosition.delete())\n                                Log.w(TAG_SECURE_FACTORY, \"Issue while deleting \" + downloadedContainerFinalPath);\n\n                        if (downloadedContainer.renameTo(downloadContainerFinalPosition)) {\n\n                            // It is relevant to add this resource to the Log file of the cached remote containers.\n                            remoteContainersCacheLogger.addCachedEntryToLog(currentSingleDexPathAsURL, containerDigest + extension);\n\n                            // Successful renaming..\n                            // It is necessary to replace the current web-like singleDexPath to access the resource with the new local version.\n                            return Optional.of(downloadedContainerFinalPath);\n\n                        } else {\n                            // Renaming operation failed..\n                            // Erase downloaded container.\n                            if (!downloadedContainer.delete())\n                                Log.w(TAG_SECURE_FACTORY, \"Issue while deleting \" + downloadedContainerPath);\n\n                            return Optional.absent();\n                        }\n                    }\n                }\n            }\n        } catch (MalformedURLException e) {\n            Log.d(\n                    TAG_SECURE_FACTORY,\n                    \"The provided singleDexPath \" + singleDexPath + \" is not a valid remote URL\");\n        }\n\n        return Optional.absent();\n    }\n\n    private Optional<String> processPathPointingToALocallyStoredContainer(\n            String singleDexPath, File importedContainerDir) {\n        // On the other hand when the developer provides a local URI for the container\n        // SecureLoaderFactory has to import this file into an application private folder on the device.\n\n        // At first compute the digest on the provided local container.\n        String encodedContainerDigest = null;\n\n        // If a container exists on the device storage, compute its digest.\n        if (new File(singleDexPath).exists()) encodedContainerDigest = computeDigestFromFilePath(singleDexPath);\n\n        // Take this branch if the digest was correctly computed on the container..\n        if (encodedContainerDigest != null) {\n\n            // Compute the extension of the file.\n            String extension = extractExtensionFromFilePath(singleDexPath).get();\n\n            // Check if a file whose name is \"encodedContainerDigest.(jar/apk)\" is already present in\n            // the cached certificate folder.\n            File[] matchingContainerArray = importedContainerDir.listFiles(new FileFilterByNameMatch(encodedContainerDigest, extension));\n\n            if (matchingContainerArray != null && matchingContainerArray.length > 0) {\n\n                // A cached version of the container already exists.\n                // So simply use that cached version\n                return Optional.of(matchingContainerArray[0].getAbsolutePath());\n            }\n            else {\n\n                // No cached copy of the container in the directory.\n                // So it is necessary to import the container into the application folder\n                // before using it.\n                InputStream inStream = null;\n                OutputStream outStream = null;\n                String cachedContainerPath = importedContainerDir.getAbsolutePath() + File.separator + encodedContainerDigest + extension;\n\n                try {\n\n                    inStream = new FileInputStream(singleDexPath);\n                    outStream = new FileOutputStream(cachedContainerPath);\n\n                    byte[] buf = new byte[8192];\n                    int length;\n\n                    // Copying the external container into the application\n                    // private folder.\n                    while ((length = inStream.read(buf)) > 0) {\n                        outStream.write(buf, 0, length);\n                    }\n\n                    // In the end add the internal singleDexPath of the container\n                    return Optional.of(cachedContainerPath);\n\n                } catch (FileNotFoundException e) {\n                    Log.w(TAG_SECURE_FACTORY, \"Problem in locating container to import in the application private folder!\");\n                } catch (IOException e) {\n                    Log.w(TAG_SECURE_FACTORY, \"Problem while importing a local container into the application private folder!\");\n                } finally {\n\n                    try {\n\n                        if (inStream != null) {\n                            inStream.close();\n                        }\n                        if (outStream != null) {\n                            outStream.close();\n                        }\n\n                    } catch (IOException e) {\n                        Log.w(TAG_SECURE_FACTORY, \"Issue in closing file streams while importing a container!\");\n                    }\n                }\n            }\n        }\n\n        return Optional.absent();\n    }\n\n    // Given the path of a file this function returns the encoded base 64 of SHA-1 digest of the file.\n\tprivate String computeDigestFromFilePath(String filePath) {\n\t\t\n\t\tFileInputStream inStream = null;\n\t\tString digestString = null;\n\t\t\n\t\ttry {\n\t\t\t// A stream used to parse the bytes of the file\n\t\t\tinStream = new FileInputStream(filePath);\n\t\t\t\n\t\t\tbyte[] buffer = new byte[8192];\n\t\t    int length;\n\t\t    while( (length = inStream.read(buffer)) != -1 ) {\n\t\t    \t// File is loaded by considering chunks of it..\n\t\t    \tmessageDigest.update(buffer, 0, length);\n\t\t    }\n\t\t    // The digest is finally computed..\n\t\t    byte[] digestBytes = messageDigest.digest();\n\t\t    \n\t\t    // ..and translated into a human readable string through Base64 encoding (Url safe).\n\t\t    // Also remove the last /n part of the string\n\t\t    digestString = Base64.encodeToString(digestBytes, Base64.URL_SAFE)\n                    .replace(System.getProperty(\"line.separator\"), \"\")\n                    .replace(\"\\r\", \"\");\n\n\t\t} catch (FileNotFoundException e) {\n\t\t\tLog.w(TAG_SECURE_FACTORY, \"No file found at \" + filePath);\n\t\t} catch (IOException e) {\n\t\t\tLog.w(TAG_SECURE_FACTORY, \"Something went wrong while calculating the digest!\");\n\t\t} finally {\n\t\t\tif (inStream != null) {\n\t\t\t\ttry {\n\t\t\t\t\tinStream.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLog.w(TAG_SECURE_FACTORY, \"Issue while closing file stream in message digest computation!\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Finally return the digest string..\n\t\treturn digestString;\n\t}\n\n\tprivate Map<String, URL> processPackageNameToCertificateMap(\n            Map<String, URL> packageNameToCertificateMap) {\n\t\tMap<String, URL> sanitizedPackageNameToCertificateMap = new HashMap<>();\n\t\t\n        for (String currentPackageName : packageNameToCertificateMap.keySet()) {\n            if (isAValidPackageName(currentPackageName)) {\n                try {\n                    URL certificateURL = packageNameToCertificateMap.get(currentPackageName);\n\n                    if (certificateURL != null) {\n                        // If the URL for the certificate is present, check that it is valid one,\n                        // and enforce HTTPS protocol..\n                        if (certificateURL.getProtocol().equals(HTTP_PROTOCOL_STRING) ||\n                                certificateURL.getProtocol().equals(HTTPS_PROTOCOL_STRING)) {\n                            sanitizedPackageNameToCertificateMap.put(\n                                    currentPackageName,\n                                    new URL(\n                                            HTTPS_PROTOCOL_STRING,\n                                            certificateURL.getHost(),\n                                            certificateURL.getPort(),\n                                            certificateURL.getFile()));\n                        }\n                    } else {\n                        // Otherwise, revert the package name, and use it as the certificate URL.\n                        sanitizedPackageNameToCertificateMap.put(\n                                currentPackageName, revertPackageNameToURL(currentPackageName));\n                    }\n                } catch (MalformedURLException e) {\n                    // Ignore this package name if an exception on the URL is raised.\n                    Log.w(TAG_SECURE_FACTORY, \"Issue while enforcing certificate URL \" +\n                            packageNameToCertificateMap.get(currentPackageName) +\n                            \" to use HTTPS protocol\");\n                }\n            }\n        }\n\t\t\n\t\treturn sanitizedPackageNameToCertificateMap;\n\t}\n\n\tprivate String downloadContainerIntoFolder(String urlPath, File resOutputDir) {\n\t\t\n\t\t// Precondition check on URL path variable..\n\t\tif (urlPath == null) return null;\n\t\t\n\t\t// Precondition check on the output local folder..\n\t\tif (resOutputDir == null || !resOutputDir.exists()) return null;\n\t\tif (!resOutputDir.isDirectory() || !resOutputDir.canRead() || !resOutputDir.canWrite()) return null;\n\n        if (!isARemoteHttpOrHttpsResource(urlPath)) return null;\n\n        URL url;\n        try {\n            url = new URL(urlPath);\n        } catch (MalformedURLException e) {\n            e.printStackTrace();\n            return null;\n        }\n\n        // Check whether the path of the file pointed by the URL is not empty\n        if (url.getFile().isEmpty()) return null;\n\n        String containerName = extractFileNameFromFilePath(url.getFile());\n\n        // Check whether the name of the file pointed by the URL is not empty\n        if (containerName.isEmpty()) return null;\n\t\t\n\t\t// Check whether the selected resource is a container (jar or apk)\n        Optional<String> optionalContainerExtension = extractExtensionFromFilePath(containerName);\n\n        if (optionalContainerExtension.isPresent() &&\n                !endsWithJarOrApkExtension(optionalContainerExtension.get())) {\n            return null;\n        }\n\n        // Check that no file is present at this location.\n\t\tFile checkFile = new File(resOutputDir.getAbsolutePath() + containerName);\n\t\t\n\t\t// In case, just delete the old file..\n\t\tif (checkFile.exists())\n\t\t\tcheckFile.delete();\n\t\t\n\t\t// Finally the container file can be downloaded from the URL\n\t\t// and stored in the local folder\n\t\tString localContainerPath = resOutputDir.getAbsolutePath() + containerName;\n\n        FileDownloader fileDownloader = new FileDownloader(context);\n\t\t\n\t\t// Redirect may be allowed here while downloading a remote container..\n\t\tboolean isDownloadSuccessful = fileDownloader.downloadRemoteResource(url, localContainerPath, true);\n\t\t\n\t\tif (isDownloadSuccessful) {\n\t\t\t\n\t\t\t// If this branch is reached, the download worked properly and the path of the output\n\t\t\t// file container is returned.\n\t\t\tif (!optionalContainerExtension.isPresent()) {\n\n\t\t\t\t// In such a situation, try to identify the extension of the downloaded file\n\t\t\t\tOptional<String> retrievedFileExtension = fileDownloader.getDownloadedFileExtension();\n\t\t\t\t\n\t\t\t\t// Check that an extension was found and it is a suitable one..\n\t\t\t\tif (retrievedFileExtension.isPresent() &&\n                        endsWithJarOrApkExtension(retrievedFileExtension.get())) {\n\n\t\t\t\t\t// In such a case rename the previous file by adding the extension\n\t\t\t\t\tFile containerToRename = new File(localContainerPath);\n\t\t\t\t\tFile finalContainerWithExtension = new File(localContainerPath + retrievedFileExtension);\n\t\t\t\t\t\n\t\t\t\t\tif (finalContainerWithExtension.exists())\n\t\t\t\t\t\tif (!finalContainerWithExtension.delete())\n\t\t\t\t\t\t\tLog.w(TAG_SECURE_FACTORY, \"Issue while deleting \" + finalContainerWithExtension);\n\t\t\t\t\t\n\t\t\t\t\tif (!containerToRename.renameTo(finalContainerWithExtension)) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Renaming operation failed..\n\t\t\t\t\t\t// Erase downloaded container.\n\t\t\t\t\t\tif (!containerToRename.delete())\n\t\t\t\t\t\t\tLog.w(TAG_SECURE_FACTORY, \"Issue while deleting \" + localContainerPath);\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t// Return the local path to the renamed container.\n\t\t\t\t\treturn localContainerPath + retrievedFileExtension;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\t// An extension is already present so just return the path..\n\t\t\t\treturn localContainerPath;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Return null if any of the download steps failed.\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "gnr/app/src/main/res/values-v11/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme for API 11+. This theme completely replaces\n        AppBaseTheme from res/values/styles.xml on API 11+ devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"Theme.AppCompat.Light\">\n        <!-- API 11 theme customizations can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "gnr/app/src/main/res/values-v14/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme for API 14+. This theme completely replaces\n        AppBaseTheme from BOTH res/values/styles.xml and\n        res/values-v11/styles.xml on API 14+ devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- API 14 theme customizations can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/CacheLoggerTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.equalTo;\n\nimport com.google.common.base.Optional;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport java.io.IOException;\nimport java.net.URL;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CacheLoggerTest {\n    private static final int POSITIVE_INTEGER_FOR_DAYS_TILL_CONSIDERED_FRESH = 1;\n    private static final String TEST_REMOTE_URL_AS_STRING = \"https://test.com/myExample.jar\";\n    private static final String ANOTHER_TEST_REMOTE_URL_AS_STRING = \"https://test2.com/anotherExample.apk\";\n\n    @Rule\n    public TemporaryFolder temporaryFolder = new TemporaryFolder();\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenNotPositiveIntegerForDaysTillConsideredFresh_whenCreateCacheLogger_thenThrows() {\n        // GIVEN\n        int notPositiveIntegerForDaysTillConsideredFresh = -1;\n\n        // WHEN\n        new CacheLogger(getTemporaryFolderPath(), notPositiveIntegerForDaysTillConsideredFresh);\n    }\n\n    @Test\n    public void givenNoHelperFileWasStored_whenCheckForCachedEntry_thenReturnsAnAbsentOptional()\n            throws Exception {\n        // GIVEN\n        final CacheLogger testCacheLogger = initializeCacheLoggerWithTestValues();\n\n        // WHEN\n        final Optional<String> optionalLocalFileNameOfTheCachedResource =\n                testCacheLogger.checkForCachedEntry(new URL(TEST_REMOTE_URL_AS_STRING));\n\n        // THEN\n        assertThat(optionalLocalFileNameOfTheCachedResource, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenNoHelperFileWasStoredAndAddAURLAsACachedEntry_whenCheckForCachedEntryWithADifferentURL_thenReturnsAnAbsentOptional()\n            throws Exception {\n        // GIVEN\n        final CacheLogger testCacheLogger = initializeCacheLoggerWithTestValues();\n        final URL testRemoteURL = new URL(TEST_REMOTE_URL_AS_STRING);\n        createFakeLocalFileAndAddItsNameToTheCacheLogger(testCacheLogger, testRemoteURL);\n\n        // WHEN\n        final Optional<String> optionalLocalFileNameOfTheCachedResource =\n                testCacheLogger.checkForCachedEntry(new URL(ANOTHER_TEST_REMOTE_URL_AS_STRING));\n\n        // THEN\n        assertThat(optionalLocalFileNameOfTheCachedResource, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenNoHelperFileWasStoredAndAddAURLAsACachedEntry_whenCheckForCachedEntryWithThatURL_thenReturnsTheLocalFileName()\n            throws Exception {\n        // GIVEN\n        final CacheLogger testCacheLogger = initializeCacheLoggerWithTestValues();\n        final URL testRemoteURL = new URL(TEST_REMOTE_URL_AS_STRING);\n        final String testLocalFileName =\n                createFakeLocalFileAndAddItsNameToTheCacheLogger(testCacheLogger, testRemoteURL);\n\n        // WHEN\n        final Optional<String> optionalLocalFileNameOfTheCachedResource =\n                testCacheLogger.checkForCachedEntry(testRemoteURL);\n\n        // THEN\n        assertThat(optionalLocalFileNameOfTheCachedResource, equalTo(Optional.of(testLocalFileName)));\n    }\n\n    @Test\n    public void givenNoHelperFileWasStoredAndCacheLoggerIsFinalizedAndAURLIsAddedAsACachedEntry_whenCheckForCachedEntryWithThatURL_thenReturnsAnAbsentOptional()\n            throws Exception {\n        // GIVEN\n        final CacheLogger testCacheLogger = initializeCacheLoggerWithTestValues();\n        testCacheLogger.finalizeLog();\n        final URL testRemoteURL = new URL(TEST_REMOTE_URL_AS_STRING);\n        createFakeLocalFileAndAddItsNameToTheCacheLogger(testCacheLogger, testRemoteURL);\n\n        // WHEN\n        final Optional<String> optionalLocalFileNameOfTheCachedResource =\n                testCacheLogger.checkForCachedEntry(testRemoteURL);\n\n        // THEN\n        assertThat(optionalLocalFileNameOfTheCachedResource, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenHelperFileWasStoredWithAURLAsACachedEntry_whenCheckForCachedEntryWithThatURLOnANewCacheLogger_thenReturnsTheLocalFileName()\n            throws Exception {\n        // GIVEN\n        final CacheLogger testCacheLogger = initializeCacheLoggerWithTestValues();\n        final URL testRemoteURL = new URL(TEST_REMOTE_URL_AS_STRING);\n        final String testLocalFileName =\n                createFakeLocalFileAndAddItsNameToTheCacheLogger(testCacheLogger, testRemoteURL);\n        testCacheLogger.finalizeLog();\n\n        final CacheLogger anotherTestCacheLogger = initializeCacheLoggerWithTestValues();\n\n        // WHEN\n        final Optional<String> optionalLocalFileNameOfTheCachedResource =\n                anotherTestCacheLogger.checkForCachedEntry(testRemoteURL);\n\n        // THEN\n        assertThat(optionalLocalFileNameOfTheCachedResource, equalTo(Optional.of(testLocalFileName)));\n    }\n\n    private CacheLogger initializeCacheLoggerWithTestValues() {\n        return new CacheLogger(\n                getTemporaryFolderPath(), POSITIVE_INTEGER_FOR_DAYS_TILL_CONSIDERED_FRESH);\n    }\n\n    private String getTemporaryFolderPath() {\n        return temporaryFolder.getRoot().getPath();\n    }\n\n    private String createFakeLocalFileAndAddItsNameToTheCacheLogger(\n            CacheLogger testCacheLogger, URL testRemoteURL) throws IOException {\n        final String testLocalFileName = computeFileNameFromURL(testRemoteURL);\n        temporaryFolder.newFile(testLocalFileName);\n        testCacheLogger.addCachedEntryToLog(testRemoteURL, testLocalFileName);\n        return testLocalFileName;\n    }\n\n    private String computeFileNameFromURL(URL testRemoteURL) {\n        return testRemoteURL.getPath().substring(1);\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/CertificateFileFilterByNameMatchTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static it.necst.grabnrun.CertificateFileFilterByNameMatch.PEM_EXTENSION;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.when;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport java.io.File;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class CertificateFileFilterByNameMatchTest {\n\n    public static final String EMPTY_CERTIFICATE_NAME = \"\";\n    private static final String TEST_CERTIFICATE_NAME = \"testCertificate\";\n    private static final String CERTIFICATE_WITH_MATCHING_NAME_BUT_UNSUPPORTED_EXTENSION =\n            TEST_CERTIFICATE_NAME + \".cert\";\n    private static final String CERTIFICATE_WITH_SUPPORTED_PEM_EXTENSION_BUT_NOT_MATCHING_NAME =\n            \"anotherTestCertificate\" + PEM_EXTENSION;\n    private static final String CERTIFICATE_WITH_MATCHING_NAME_AND_SUPPORTED_PEM_EXTENSION =\n            TEST_CERTIFICATE_NAME + PEM_EXTENSION;\n\n    @Mock File fileMock;\n    CertificateFileFilterByNameMatch testCertFileFilter;\n\n    @Before\n    public void initializeCertificateFileFilterWithTestCertificateName() {\n        testCertFileFilter = new CertificateFileFilterByNameMatch(TEST_CERTIFICATE_NAME);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFileName_whenCreateCertificateFileFilter_thenThrows() {\n        new CertificateFileFilterByNameMatch(EMPTY_CERTIFICATE_NAME);\n    }\n\n    @Test\n    public void givenADirectory_whenAccept_thenReturnFalse() {\n        // GIVEN\n        setupFileMockAsADirectory(true);\n\n        // WHEN\n        boolean isAccepted = testCertFileFilter.accept(fileMock);\n\n        // THEN\n        assertFalse(isAccepted);\n    }\n\n    @Test\n    public void givenAFileWithMatchingFileNameButUnsupportedExtension_whenAccept_thenReturnFalse() {\n        // GIVEN\n        setupFileMockAsADirectory(false);\n        when(fileMock.getName())\n                .thenReturn(CERTIFICATE_WITH_MATCHING_NAME_BUT_UNSUPPORTED_EXTENSION);\n\n        // WHEN\n        boolean isAccepted = testCertFileFilter.accept(fileMock);\n\n        // THEN\n        assertFalse(isAccepted);\n    }\n\n    @Test\n    public void givenAFileWithSupportedExtensionButNotMatchingFileName_whenAccept_thenReturnFalse() {\n        // GIVEN\n        setupFileMockAsADirectory(false);\n        when(fileMock.getName())\n                .thenReturn(CERTIFICATE_WITH_SUPPORTED_PEM_EXTENSION_BUT_NOT_MATCHING_NAME);\n\n        // WHEN\n        boolean isAccepted = testCertFileFilter.accept(fileMock);\n\n        // THEN\n        assertFalse(isAccepted);\n    }\n\n    @Test\n    public void givenAFileWithMatchingFileNameAndSupportedExtension_whenAccept_thenReturnTrue() {\n        // GIVEN\n        setupFileMockAsADirectory(false);\n        when(fileMock.getName())\n                .thenReturn(CERTIFICATE_WITH_MATCHING_NAME_AND_SUPPORTED_PEM_EXTENSION);\n\n        // WHEN\n        boolean isAccepted = testCertFileFilter.accept(fileMock);\n\n        // THEN\n        assertTrue(isAccepted);\n    }\n\n    private void setupFileMockAsADirectory(boolean shouldBeADirectory) {\n        when(fileMock.isDirectory()).thenReturn(shouldBeADirectory);\n        when(fileMock.isFile()).thenReturn(!shouldBeADirectory);\n    }\n}"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/ContainerSignatureVerifierTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static android.content.Context.CONNECTIVITY_SERVICE;\nimport static it.necst.grabnrun.FileHelper.extractFileNameFromFilePath;\nimport static it.necst.grabnrun.SecureLoaderFactory.X_509_CERTIFICATE;\nimport static junit.framework.Assert.assertFalse;\nimport static junit.framework.Assert.assertNotNull;\nimport static junit.framework.Assert.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\n\n// TODO(falsinal): Once the code of the tested classes will be simplified, evaluate\n// whether to add extra test cases for APK containers\n\n@RunWith(MockitoJUnitRunner.class)\npublic class ContainerSignatureVerifierTest {\n\n    @Rule public TemporaryFolder temporaryResourceFolder = new TemporaryFolder();\n\n    private static final String EMPTY_CONTAINER_PATH = \"\";\n\n    private static final String UNSIGNED_JAR_CONTAINER_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/s/ofl0q47rhggfugg/unsigned-jsoup-1.8.3.jar\";\n    private static final String REPACKAGED_JAR_CONTAINER_SIGNED_WITH_TRUSTED_CERTIFICATE_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/s/dm0v5gnrli6fu31/componentModifierRepack.jar\";\n    private static final String JAR_CONTAINER_SIGNED_WITH_TRUSTED_CERTIFICATE_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/u/28681922/componentModifier.jar\";\n    private static final String TRUSTED_CERTIFICATE_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\";\n    private static final String A_DIFFERENT_TRUSTED_CERTIFICATE_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/s/px59dqbwsbhvln5/verify_cert.pem\";\n\n    @Mock PackageManager mockPackageManager = mock(PackageManager.class);\n    @Mock Context mockContext = mock(Context.class);\n    @Mock ConnectivityManager mockConnectivityManager = mock(ConnectivityManager.class);\n    @Mock NetworkInfo mockNetworkInfo = mock(NetworkInfo.class);\n\n    ContainerSignatureVerifier testContainerSignatureVerifier;\n    X509Certificate trustedCertificate;\n\n    @Before\n    public void initializeTestContainerSignatureVerifierAndSetupMocks() throws Exception {\n\n        testContainerSignatureVerifier = new ContainerSignatureVerifier(\n                mockPackageManager, CertificateFactory.getInstance(X_509_CERTIFICATE));\n\n        when(mockContext.getSystemService(CONNECTIVITY_SERVICE))\n                .thenReturn(mockConnectivityManager);\n        when(mockConnectivityManager.getActiveNetworkInfo()).thenReturn(mockNetworkInfo);\n        when(mockNetworkInfo.isConnected()).thenReturn(true);\n\n        trustedCertificate = retrieveAndGenerateTrustedCertificate(TRUSTED_CERTIFICATE_URL_AS_STRING);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyContainerPath_whenVerifyContainerAgainstCertificate_thenThrows() {\n        // GIVEN + WHEN\n        testContainerSignatureVerifier\n                .verifyContainerSignatureAgainstCertificate(EMPTY_CONTAINER_PATH, trustedCertificate);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnUnsignedJarContainer_whenVerifyContainerAgainstCertificate_thenThrows() {\n        // GIVEN\n        String containerPathPointingToNoFile = temporaryResourceFolder.getRoot()\n                + File.pathSeparator + \"NotExistingContainer.jar\";\n        assertFalse(new File(containerPathPointingToNoFile).exists());\n\n        // WHEN\n        testContainerSignatureVerifier\n                .verifyContainerSignatureAgainstCertificate(\n                        containerPathPointingToNoFile, trustedCertificate);\n    }\n\n    @Test\n    public void givenAnUnsignedJarContainer_whenVerifyContainerAgainstCertificate_thenReturnsFalse() throws Exception {\n        // GIVEN + WHEN\n        boolean isContainerVerificationSuccessful =\n                testContainerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                        downloadRemoteResourceIntoTemporaryFolder(\n                                UNSIGNED_JAR_CONTAINER_URL_AS_STRING),\n                        trustedCertificate);\n\n        // THEN\n        assertFalse(isContainerVerificationSuccessful);\n    }\n    @Test\n    public void givenAJarContainerSignedWithAnOtherTrustedCertificate_whenVerifyContainerAgainstCertificate_thenReturnsFalse() throws Exception {\n        // GIVEN\n        X509Certificate aDifferentTrustedCertificate =\n                retrieveAndGenerateTrustedCertificate(A_DIFFERENT_TRUSTED_CERTIFICATE_URL_AS_STRING);\n\n        // WHEN\n        boolean isContainerVerificationSuccessful =\n                testContainerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                        downloadRemoteResourceIntoTemporaryFolder(\n                                JAR_CONTAINER_SIGNED_WITH_TRUSTED_CERTIFICATE_URL_AS_STRING),\n                        aDifferentTrustedCertificate);\n\n        // THEN\n        assertFalse(isContainerVerificationSuccessful);\n    }\n\n    @Test\n    public void givenARepackagedJarContainerPreviouslySignedWithTheTrustedCertificate_whenVerifyContainerAgainstCertificate_thenReturnsFalse() throws Exception {\n        // GIVEN + WHEN\n        boolean isContainerVerificationSuccessful =\n                testContainerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                        downloadRemoteResourceIntoTemporaryFolder(\n                                REPACKAGED_JAR_CONTAINER_SIGNED_WITH_TRUSTED_CERTIFICATE_URL_AS_STRING),\n                        trustedCertificate);\n\n        // THEN\n        assertFalse(isContainerVerificationSuccessful);\n    }\n\n    @Test\n    public void givenAJarContainerSignedWithTheTrustedCertificate_whenVerifyContainerAgainstCertificate_thenReturnsTrue() throws Exception {\n        // GIVEN + WHEN\n        boolean isContainerVerificationSuccessful =\n                testContainerSignatureVerifier.verifyContainerSignatureAgainstCertificate(\n                        downloadRemoteResourceIntoTemporaryFolder(\n                                JAR_CONTAINER_SIGNED_WITH_TRUSTED_CERTIFICATE_URL_AS_STRING),\n                        trustedCertificate);\n\n        // THEN\n        assertTrue(isContainerVerificationSuccessful);\n    }\n\n    private X509Certificate retrieveAndGenerateTrustedCertificate(\n            String remoteCertificateURLAsString) throws Exception {\n        String trustedCertificatePath =\n                downloadRemoteResourceIntoTemporaryFolder(remoteCertificateURLAsString);\n\n        InputStream inStream = new FileInputStream(trustedCertificatePath);\n        X509Certificate trustedCertificate = (X509Certificate) CertificateFactory\n                .getInstance(X_509_CERTIFICATE)\n                .generateCertificate(inStream);\n\n        assertNotNull(inStream);\n        inStream.close();\n\n        return trustedCertificate;\n    }\n\n    private String downloadRemoteResourceIntoTemporaryFolder(String remoteURLAsString) throws Exception {\n        URL remoteURL = new URL(remoteURLAsString);\n\n        String localContainerURI = temporaryResourceFolder.getRoot().toString()\n                + File.separator + extractFileNameFromFilePath(remoteURL.getPath());\n\n        boolean isRemoteResourceSuccessful = new FileDownloader(mockContext).downloadRemoteResource(\n                remoteURL,\n                localContainerURI,\n                false);\n\n        assertTrue(isRemoteResourceSuccessful);\n        return localContainerURI;\n    }\n\n}"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/DexPathStringProcessorTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static junit.framework.Assert.assertFalse;\nimport static junit.framework.Assert.assertTrue;\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport java.io.File;\nimport java.util.NoSuchElementException;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class DexPathStringProcessorTest {\n\n    private static final String TEST_EMPTY_PATH = \"\";\n\n    private static final String TEST_LOCAL_PATH = \"/example/path/for/test/file.jar\";\n    private static final String TEST_REMOTE_HTTP_PATH = \"http://myexample.com/file.apk\";\n    private static final String TEST_REMOTE_HTTPS_PATH = \"https://myexample.com/file.apk\";\n    private static final String TEST_COMPOSITE_DEX_PATH = concatenateDexPathStrings(\n            TEST_LOCAL_PATH, TEST_REMOTE_HTTP_PATH, TEST_REMOTE_HTTPS_PATH);\n\n    private DexPathStringProcessor testDexPathStringProcessor;\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyDexPath_whenCreateDexPathStringProcessor_thenThrows() {\n        // WHEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_EMPTY_PATH);\n    }\n\n    @Test\n    public void givenASingleTestLocalPath_whenHasNextDexPathString_thenReturnsTrue() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_LOCAL_PATH);\n\n        // WHEN\n        final boolean hasNextDexPathString = testDexPathStringProcessor.hasNextDexPathString();\n\n        // THEN\n        assertTrue(hasNextDexPathString);\n    }\n\n    @Test\n    public void givenASingleTestLocalPath_whenNextDexPathString_thenReturnsTheTestLocalPath() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_LOCAL_PATH);\n\n        // WHEN\n        final String nextDexPathString = testDexPathStringProcessor.nextDexPathString();\n\n        // THEN\n        assertThat(nextDexPathString, is(equalTo(TEST_LOCAL_PATH)));\n    }\n\n    @Test\n    public void givenASingleTestRemoteHttpPath_whenHasNextDexPathString_thenReturnsTrue() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_REMOTE_HTTP_PATH);\n\n        // WHEN\n        final boolean hasNextDexPathString = testDexPathStringProcessor.hasNextDexPathString();\n\n        // THEN\n        assertTrue(hasNextDexPathString);\n    }\n\n    @Test\n    public void givenASingleTestRemoteHttpPath_whenNextDexPathString_thenReturnsTheTestRemoteHttpPath() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_REMOTE_HTTP_PATH);\n\n        // WHEN\n        final String nextDexPathString = testDexPathStringProcessor.nextDexPathString();\n\n        // THEN\n        assertThat(nextDexPathString, is(equalTo(TEST_REMOTE_HTTP_PATH)));\n    }\n\n    @Test\n    public void givenASingleTestRemoteHttpsPath_whenHasNextDexPathString_thenReturnsTrue() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_REMOTE_HTTPS_PATH);\n\n        // WHEN\n        final boolean hasNextDexPathString = testDexPathStringProcessor.hasNextDexPathString();\n\n        // THEN\n        assertTrue(hasNextDexPathString);\n    }\n\n    @Test\n    public void givenASingleTestRemoteHttpsPath_whenNextDexPathString_thenReturnsTheTestRemoteHttpsPath() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_REMOTE_HTTPS_PATH);\n\n        // WHEN\n        final String nextDexPathString = testDexPathStringProcessor.nextDexPathString();\n\n        // THEN\n        assertThat(nextDexPathString, is(equalTo(TEST_REMOTE_HTTPS_PATH)));\n    }\n\n    @Test\n    public void givenATestDexPathWhichConcatenatesThreePaths_whenHasNextDexPathString_thenReturnsTrueThreeTimesAndFalseAtTheFourth() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_COMPOSITE_DEX_PATH);\n\n        // WHEN + THEN\n        assertTrue(testDexPathStringProcessor.hasNextDexPathString());\n        testDexPathStringProcessor.nextDexPathString();\n        assertTrue(testDexPathStringProcessor.hasNextDexPathString());\n        testDexPathStringProcessor.nextDexPathString();\n        assertTrue(testDexPathStringProcessor.hasNextDexPathString());\n        testDexPathStringProcessor.nextDexPathString();\n        assertFalse(testDexPathStringProcessor.hasNextDexPathString());\n    }\n\n    @Test\n    public void givenATestDexPathWhichConcatenatesThreePaths_whenNextDexPathString_thenReturnsThreeStringsInTheSameOrderOfTheInputDexPath() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_COMPOSITE_DEX_PATH);\n\n        // WHEN + THEN\n        assertThat(testDexPathStringProcessor.nextDexPathString(), is(equalTo(TEST_LOCAL_PATH)));\n        assertThat(testDexPathStringProcessor.nextDexPathString(), is(equalTo(TEST_REMOTE_HTTP_PATH)));\n        assertThat(testDexPathStringProcessor.nextDexPathString(), is(equalTo(TEST_REMOTE_HTTPS_PATH)));\n    }\n\n    @Test (expected = NoSuchElementException.class)\n    public void givenATestDexPathWhichConcatenatesThreePaths_whenNextDexPathString_thenThrowsAfterThirdInvocation() {\n        // GIVEN\n        testDexPathStringProcessor = new DexPathStringProcessor(TEST_COMPOSITE_DEX_PATH);\n\n        // WHEN\n        testDexPathStringProcessor.nextDexPathString();\n        testDexPathStringProcessor.nextDexPathString();\n        testDexPathStringProcessor.nextDexPathString();\n        testDexPathStringProcessor.nextDexPathString();\n    }\n\n    private static String concatenateDexPathStrings(String... dexPathStrings) {\n        StringBuilder dexPathComplexStringBuilder = new StringBuilder();\n\n        for (String dexPathString : dexPathStrings) {\n            dexPathComplexStringBuilder.append(dexPathString).append(File.pathSeparator);\n        }\n\n        return dexPathComplexStringBuilder\n                .deleteCharAt(dexPathComplexStringBuilder.lastIndexOf(File.pathSeparator))\n                .toString();\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/FileDownloaderTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport android.content.Context;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.test.mock.MockContext;\n\nimport com.google.common.base.Optional;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport java.io.File;\nimport java.net.URL;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FileDownloaderTest {\n\n    private static final String TEST_REMOTE_CERTIFICATE_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\";\n    private static final String TEST_REMOTE_CERTIFICATE_URL_NEEDING_REDIRECT_AS_STRING =\n            \"http://bit.ly/1Yh5kft\";\n    private static final String TEST_REMOTE_URL_USING_UNSUPPORTED_FTP_PROTOCOL_AS_STRING =\n            \"ftp://ftp.funet.fi/pub/standards/RFC/rfc959.txt\";\n\n    private static final String LOCAL_FILE_NAME = \"localFile\";\n\n    @Rule public TemporaryFolder temporaryTestFolder = new TemporaryFolder();\n\n    @Mock MockContext mockContext = new MockContext();\n    @Mock ConnectivityManager mockConnectivityManager = mock(ConnectivityManager.class);\n    @Mock NetworkInfo mockNetworkInfo = mock(NetworkInfo.class);\n\n    FileDownloader testFileDownloader;\n\n    @Before\n    public void setupMocksForFileDownloader() {\n        when(mockContext.getSystemService(Context.CONNECTIVITY_SERVICE))\n                .thenReturn(mockConnectivityManager);\n        when(mockConnectivityManager.getActiveNetworkInfo()).thenReturn(mockNetworkInfo);\n        when(mockNetworkInfo.isConnected()).thenReturn(true);\n        testFileDownloader = new FileDownloader(mockContext);\n    }\n\n    @Test\n    public void givenNoNetworkConnectivityIsAvailable_whenDownloadRemoteResource_thenDownloadFailsAndRetrievesNoFile()\n            throws Exception {\n        // GIVEN\n        when(mockNetworkInfo.isConnected()).thenReturn(false);\n        final String localFileURIInTemporaryTestDirectory =\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME);\n\n        // WHEN\n        final boolean wasDownloadSuccessful = testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING),\n                localFileURIInTemporaryTestDirectory,\n                false);\n\n        // THEN\n        assertFalse(wasDownloadSuccessful);\n        assertFalse(fileExists(localFileURIInTemporaryTestDirectory));\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenNetworkConnectivityIsAvailable_whenDownloadRemoteResourceWithTestURLUsingUnsupportedProtocol_thenThrows()\n            throws Exception {\n        // GIVEN\n        final String localFileURIInTemporaryTestDirectory =\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME);\n\n        // WHEN\n        testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_URL_USING_UNSUPPORTED_FTP_PROTOCOL_AS_STRING),\n                localFileURIInTemporaryTestDirectory,\n                false);\n    }\n\n    @Test\n    public void givenNetworkConnectivityIsAvailable_whenDownloadRemoteResourceWithTestRemoteCertificateURL_thenDownloadSucceedsAndRetrievesAFile()\n            throws Exception {\n        // GIVEN\n        final String localFileURIInTemporaryTestDirectory =\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME);\n\n        // WHEN\n        final boolean wasDownloadSuccessful = testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING),\n                localFileURIInTemporaryTestDirectory,\n                false);\n\n        // THEN\n        assertTrue(wasDownloadSuccessful);\n        assertTrue(fileExists(localFileURIInTemporaryTestDirectory));\n    }\n\n    @Test\n    public void givenNetworkConnectivityIsAvailableAndNoRedirectIsAllowed_whenDownloadRemoteResourceWithTestRemoteCertificateURLNeedingRedirect_thenDownloadFailsAndRetrievesNoFile()\n            throws Exception {\n        // GIVEN\n        final String localFileURIInTemporaryTestDirectory =\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME);\n        final boolean redirectIsNotAllowed = false;\n\n        // WHEN\n        final boolean wasDownloadSuccessful = testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_CERTIFICATE_URL_NEEDING_REDIRECT_AS_STRING),\n                localFileURIInTemporaryTestDirectory,\n                redirectIsNotAllowed);\n\n        // THEN\n        assertFalse(wasDownloadSuccessful);\n        assertFalse(fileExists(localFileURIInTemporaryTestDirectory));\n    }\n\n    @Test\n    public void givenNetworkConnectivityIsAvailableAndRedirectIsAllowed_whenDownloadRemoteResourceWithTestRemoteCertificateURLNeedingRedirect_thenDownloadSucceedsAndRetrievesAFile()\n            throws Exception {\n        // GIVEN\n        final String localFileURIInTemporaryTestDirectory =\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME);\n        final boolean redirectIsAllowed = true;\n\n        // WHEN\n        final boolean wasDownloadSuccessful = testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_CERTIFICATE_URL_NEEDING_REDIRECT_AS_STRING),\n                localFileURIInTemporaryTestDirectory,\n                redirectIsAllowed);\n\n        // THEN\n        assertTrue(wasDownloadSuccessful);\n        assertTrue(fileExists(localFileURIInTemporaryTestDirectory));\n    }\n\n    @Test\n    public void givenNoRemoteResourceWasPreviouslyDownloaded_whenGetDownloadedFileExtension_thenReturnsAnOptionalAbsent() {\n        // WHEN\n        final Optional<String> optionalDownloadedFileExtension =\n                testFileDownloader.getDownloadedFileExtension();\n\n        // THEN\n        assertThat(optionalDownloadedFileExtension, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenRemoteResourceWasPreviouslyDownloaded_whenGetDownloadedFileExtension_thenReturnsAnOptionalStringMatchingTheFileExtension()\n            throws Exception {\n        // GIVEN\n        testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING),\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME),\n                false);\n        // WHEN\n        final Optional<String> optionalDownloadedFileExtension =\n                testFileDownloader.getDownloadedFileExtension();\n\n        // THEN\n        final String expectedFileExtensionForCertificateFile = \".pem\";\n        assertThat(\n                optionalDownloadedFileExtension,\n                equalTo(Optional.of(expectedFileExtensionForCertificateFile)));\n    }\n\n    @Test\n    public void givenRemoteResourceRequiringRedirectWasPreviouslyDownloaded_whenGetDownloadedFileExtension_thenReturnsAnOptionalStringMatchingTheFileExtension()\n            throws Exception {\n        // GIVEN\n        final boolean redirectIsAllowed = true;\n        testFileDownloader.downloadRemoteResource(\n                new URL(TEST_REMOTE_CERTIFICATE_URL_NEEDING_REDIRECT_AS_STRING),\n                getLocalFileURIInTemporaryTestDirectory(LOCAL_FILE_NAME),\n                redirectIsAllowed);\n        // WHEN\n        final Optional<String> optionalDownloadedFileExtension =\n                testFileDownloader.getDownloadedFileExtension();\n\n        // THEN\n        final String expectedFileExtensionForCertificateFile = \".pem\";\n        assertThat(\n                optionalDownloadedFileExtension,\n                equalTo(Optional.of(expectedFileExtensionForCertificateFile)));\n    }\n\n    private String getLocalFileURIInTemporaryTestDirectory(String localFileName) {\n        return temporaryTestFolder.getRoot().getPath() + File.separator + localFileName;\n    }\n\n    private static boolean fileExists(String path) {\n        return new File(path).exists();\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/FileFilterByNameMatchTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.Mockito.when;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.mockito.runners.MockitoJUnitRunner;\n\nimport java.io.File;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FileFilterByNameMatchTest {\n\n    public static final String EMPTY_FILE_NAME = \"\";\n    private static final String TEST_FILE_NAME = \"testCertificate\";\n    private static final String INVALID_FILE_EXTENSION = \".tooLongSinceUpToFourCharsAreSupported\";\n\n    private static final String VALID_FILE_EXTENSION = \".txt\";\n    private static final String ANOTHER_VALID_FILE_EXTENSION_WITH_UPPERCASE = \".DOCX\";\n    private static final String CERTIFICATE_WITH_MATCHING_NAME_BUT_NOT_EXTENSION =\n            TEST_FILE_NAME + \".doc\";\n    private static final String CERTIFICATE_WITH_MATCHING_EXTENSION_BUT_NOT_NAME =\n            \"anotherTestFileName\" + VALID_FILE_EXTENSION;\n    private static final String CERTIFICATE_WITH_MATCHING_NAME_AND_EXTENSION =\n            TEST_FILE_NAME + VALID_FILE_EXTENSION;\n\n    @Mock File fileMock;\n    private FileFilterByNameMatch testFileFilter;\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFileName_whenCreateFileFilter_thenThrows() {\n        new FileFilterByNameMatch(EMPTY_FILE_NAME, VALID_FILE_EXTENSION);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAFileExtensionNotMatchingDotAndThreeCharacters_whenCreateFileFilter_thenThrows() {\n        new FileFilterByNameMatch(TEST_FILE_NAME, INVALID_FILE_EXTENSION);\n    }\n\n    @Test\n    public void givenAFileExtensionWithFourUppercaseCharacters_whenCreateFileFilter_thenTheTestSucceeds() {\n        new FileFilterByNameMatch(TEST_FILE_NAME, ANOTHER_VALID_FILE_EXTENSION_WITH_UPPERCASE);\n    }\n\n    @Test\n    public void givenADirectory_whenAccept_thenReturnFalse() {\n        // GIVEN\n        initializeFileFilterWithTestValues();\n        setupFileMockAsADirectory(true);\n\n        // WHEN\n        boolean isAccepted = testFileFilter.accept(fileMock);\n\n        // THEN\n        assertFalse(isAccepted);\n    }\n\n    @Test\n    public void givenAFileWithMatchingFileNameButNotExtension_whenAccept_thenReturnFalse() {\n        // GIVEN\n        initializeFileFilterWithTestValues();\n        setupFileMockAsADirectory(false);\n        when(fileMock.getName())\n                .thenReturn(CERTIFICATE_WITH_MATCHING_NAME_BUT_NOT_EXTENSION);\n\n        // WHEN\n        boolean isAccepted = testFileFilter.accept(fileMock);\n\n        // THEN\n        assertFalse(isAccepted);\n    }\n\n    @Test\n    public void givenAFileWithMatchingExtensionButNotName_whenAccept_thenReturnFalse() {\n        // GIVEN\n        initializeFileFilterWithTestValues();\n        setupFileMockAsADirectory(false);\n        when(fileMock.getName())\n                .thenReturn(CERTIFICATE_WITH_MATCHING_EXTENSION_BUT_NOT_NAME);\n\n        // WHEN\n        boolean isAccepted = testFileFilter.accept(fileMock);\n\n        // THEN\n        assertFalse(isAccepted);\n    }\n\n    @Test\n    public void givenAFileWithMatchingNameAndExtension_whenAccept_thenReturnTrue() {\n        // GIVEN\n        initializeFileFilterWithTestValues();\n        setupFileMockAsADirectory(false);\n        when(fileMock.getName())\n                .thenReturn(CERTIFICATE_WITH_MATCHING_NAME_AND_EXTENSION);\n\n        // WHEN\n        boolean isAccepted = testFileFilter.accept(fileMock);\n\n        // THEN\n        assertTrue(isAccepted);\n    }\n\n    private void initializeFileFilterWithTestValues() {\n        testFileFilter = new FileFilterByNameMatch(TEST_FILE_NAME, VALID_FILE_EXTENSION);\n    }\n\n    private void setupFileMockAsADirectory(boolean shouldBeADirectory) {\n        when(fileMock.isDirectory()).thenReturn(shouldBeADirectory);\n        when(fileMock.isFile()).thenReturn(!shouldBeADirectory);\n    }\n}"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/FileHelperTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static it.necst.grabnrun.FileHelper.endsWithJarOrApkExtension;\nimport static it.necst.grabnrun.FileHelper.extractExtensionFromFilePath;\nimport static it.necst.grabnrun.FileHelper.extractFileNameFromFilePath;\nimport static it.necst.grabnrun.FileHelper.extractFileNameWithoutExtensionFromFilePath;\nimport static it.necst.grabnrun.FileHelper.extractFilePathWithoutExtensionFromFilePath;\nimport static junit.framework.Assert.assertFalse;\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.junit.Assert.assertTrue;\n\nimport com.google.common.base.Optional;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class FileHelperTest {\n\n    private static final String TEST_FILE_PATH = \"/testPath/\";\n    private static final String TEST_FILE_NAME_WITH_NO_EXTENSION = \"testFile\";\n    private static final String TEST_FILE_EXTENSION_FOR_A_CONTAINER = FileHelper.APK_EXTENSION;\n\n    private static final String TEST_FILE_NAME_WITH_CONTAINER_EXTENSION =\n            TEST_FILE_NAME_WITH_NO_EXTENSION + TEST_FILE_EXTENSION_FOR_A_CONTAINER;\n\n    private static final String TEST_FILE_PATH_WITH_NO_EXTENSION =\n             TEST_FILE_PATH + TEST_FILE_NAME_WITH_NO_EXTENSION;\n    private static final String TEST_FILE_PATH_WITH_CONTAINER_EXTENSION =\n             TEST_FILE_PATH + TEST_FILE_NAME_WITH_CONTAINER_EXTENSION;\n\n    private static final String TEST_FILE_EXTENSION_NOT_FOR_A_CONTAINER = \".txt\";\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFilePath_whenExtractFilePathWithoutExtensionFromFilePath_thenThrows() {\n        // GIVEN\n        String emptyFilePath = \"\";\n\n        // WHEN\n        extractFilePathWithoutExtensionFromFilePath(emptyFilePath);\n    }\n\n    @Test\n    public void givenFileNameWithoutExtension_whenExtractFilePathWithoutExtensionFromFilePath_thenReturnsTheSameString() {\n        // WHEN\n        String extractedFilePath =\n                extractFilePathWithoutExtensionFromFilePath(TEST_FILE_NAME_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(extractedFilePath, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFileNameWithExtension_whenExtractFilePathWithoutExtensionFromFilePath_thenReturnsTheFileNameWithoutTheExtension() {\n        // WHEN\n        String extractedFilePath =\n                extractFilePathWithoutExtensionFromFilePath(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(extractedFilePath, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFilePathWithoutExtension_whenExtractFilePathWithoutExtensionFromFilePath_thenReturnsTheSameString() {\n        // WHEN\n        String extractedFilePath =\n                extractFilePathWithoutExtensionFromFilePath(TEST_FILE_PATH_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(extractedFilePath, is(equalTo(TEST_FILE_PATH_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFilePathWithExtension_whenExtractFilePathWithoutExtensionFromFilePath_thenReturnsTheFilePathWithoutTheExtension() {\n        // WHEN\n        String extractedFilePath =\n                extractFilePathWithoutExtensionFromFilePath(TEST_FILE_PATH_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(extractedFilePath, is(equalTo(TEST_FILE_PATH_WITH_NO_EXTENSION)));\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFilePath_whenExtractFileNameWithoutExtensionFromFilePath_thenThrows() {\n        // GIVEN\n        String emptyFilePath = \"\";\n\n        // WHEN\n        extractFileNameWithoutExtensionFromFilePath(emptyFilePath);\n    }\n\n    @Test\n    public void givenAFilePathWithoutAFileNameAndExtension_whenExtractFileNameWithoutExtensionFromFilePath_thenReturnsAnEmptyString() {\n        // WHEN\n        String extractedFileName = extractFileNameWithoutExtensionFromFilePath(TEST_FILE_PATH);\n\n        // THEN\n        assertTrue(extractedFileName.isEmpty());\n    }\n\n    @Test\n    public void givenFileNameWithoutExtension_whenExtractFileNameWithoutExtensionFromFilePath_thenReturnsTheSameString() {\n        // WHEN\n        String extractedFileName =\n                extractFileNameWithoutExtensionFromFilePath(TEST_FILE_NAME_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFileNameWithExtension_whenExtractFileNameWithoutExtensionFromFilePath_thenReturnsTheFileNameWithoutTheExtension() {\n        // WHEN\n        String extractedFileName =\n                extractFileNameWithoutExtensionFromFilePath(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFilePathWithoutExtension_whenExtractFileNameWithoutExtensionFromFilePath_thenReturnsTheFileNameWithoutTheExtension() {\n        // WHEN\n        String extractedFileName =\n                extractFileNameWithoutExtensionFromFilePath(TEST_FILE_PATH_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFilePathWithExtension_whenExtractFileNameWithoutExtensionFromFilePath_thenReturnsTheFileNameWithoutTheExtension() {\n        // WHEN\n        String extractedFileName =\n                extractFileNameWithoutExtensionFromFilePath(TEST_FILE_PATH_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFilePath_whenExtractFileNameFromFilePath_thenThrows() {\n        // GIVEN\n        String emptyFilePath = \"\";\n\n        // WHEN\n        extractFileNameFromFilePath(emptyFilePath);\n    }\n\n    @Test\n    public void givenAFilePathWithoutAFileNameAndExtension_whenExtractFileNameFromFilePath_thenReturnsAnEmptyString() {\n        // WHEN\n        String extractedFileName = extractFileNameFromFilePath(TEST_FILE_PATH);\n\n        // THEN\n        assertTrue(extractedFileName.isEmpty());\n    }\n\n    @Test\n    public void givenFileNameWithoutExtension_whenExtractFileNameFromFilePath_thenReturnsTheSameString() {\n        // WHEN\n        String extractedFileName = extractFileNameFromFilePath(TEST_FILE_NAME_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFileNameWithExtension_whenExtractFileNameFromFilePath_thenReturnsTheSameString() {\n        // WHEN\n        String extractedFileName = extractFileNameFromFilePath(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION)));\n    }\n\n    @Test\n    public void givenFilePathWithoutExtension_whenExtractFileNameFromFilePath_thenReturnsTheFileName() {\n        // WHEN\n        String extractedFileName = extractFileNameFromFilePath(TEST_FILE_PATH_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_NO_EXTENSION)));\n    }\n\n    @Test\n    public void givenFilePathWithExtension_whenExtractFileNameFromFilePath_thenReturnsTheFileName() {\n        // WHEN\n        String extractedFileName =\n                extractFileNameFromFilePath(TEST_FILE_PATH_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(extractedFileName, is(equalTo(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION)));\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFilePath_whenExtractExtensionFromFilePath_thenThrows() {\n        // GIVEN\n        String emptyFilePath = \"\";\n\n        // WHEN\n        extractExtensionFromFilePath(emptyFilePath);\n    }\n\n    @Test\n    public void givenAFileNameWithNoExtensionOnTheFile_whenExtractExtensionFromFilePath_thenReturnsAnAbsentOptional() {\n        // WHEN\n        Optional<String> optionalFileExtension =\n                extractExtensionFromFilePath(TEST_FILE_NAME_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(optionalFileExtension, is(equalTo(Optional.<String>absent())));\n    }\n\n    @Test\n    public void givenAFilePathWithNoExtensionOnTheFile_whenExtractExtensionFromFilePath_thenReturnsAnAbsentOptional() {\n        // WHEN\n        Optional<String> optionalFileExtension =\n                extractExtensionFromFilePath(TEST_FILE_PATH_WITH_NO_EXTENSION);\n\n        // THEN\n        assertThat(optionalFileExtension, is(equalTo(Optional.<String>absent())));\n    }\n\n    @Test\n    public void givenAFileNameWithExtensionForTheFile_whenExtractExtensionFromFilePath_thenReturnsAnOptionalContainingTheExtension() {\n        // WHEN\n        Optional<String> optionalFileExtension =\n                extractExtensionFromFilePath(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(optionalFileExtension, is(equalTo(Optional.of(TEST_FILE_EXTENSION_FOR_A_CONTAINER))));\n    }\n\n    @Test\n    public void givenAFilePathWithExtensionForTheFile_whenExtractExtensionFromFilePath_thenReturnsAnOptionalContainingTheExtension() {\n        // WHEN\n        Optional<String> optionalFileExtension =\n                extractExtensionFromFilePath(TEST_FILE_PATH_WITH_CONTAINER_EXTENSION);\n\n        // THEN\n        assertThat(optionalFileExtension, is(equalTo(Optional.of(TEST_FILE_EXTENSION_FOR_A_CONTAINER))));\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyFilePath_whenEndsWithJarOrApkExtension_thenThrows() {\n        // GIVEN\n        String emptyFilePath = \"\";\n\n        // WHEN\n        endsWithJarOrApkExtension(emptyFilePath);\n    }\n\n    @Test\n    public void givenAFileNameWithNoExtension_whenEndsWithJarOrApkExtension_thenReturnsFalse() {\n        // WHEN + THEN\n        assertFalse(endsWithJarOrApkExtension(TEST_FILE_NAME_WITH_NO_EXTENSION));\n    }\n\n    @Test\n    public void givenAFileNameWithANotContainerExtension_whenEndsWithJarOrApkExtension_thenReturnsFalse() {\n        // WHEN + THEN\n        assertFalse(endsWithJarOrApkExtension(\n                TEST_FILE_NAME_WITH_NO_EXTENSION + TEST_FILE_EXTENSION_NOT_FOR_A_CONTAINER));\n    }\n\n    @Test\n    public void givenAFileExtensionNotForAContainer_whenEndsWithJarOrApkExtension_thenReturnsFalse() {\n        // WHEN + THEN\n        assertFalse(endsWithJarOrApkExtension(TEST_FILE_EXTENSION_NOT_FOR_A_CONTAINER));\n    }\n\n    @Test\n    public void givenAFileExtensionForAContainer_whenEndsWithJarOrApkExtension_thenReturnsTrue() {\n        // WHEN + THEN\n        assertTrue(endsWithJarOrApkExtension(TEST_FILE_EXTENSION_FOR_A_CONTAINER));\n    }\n\n    @Test\n    public void givenAFileNameWithAContainerExtension_whenEndsWithJarOrApkExtension_thenReturnsTrue() {\n        // WHEN + THEN\n        assertTrue(endsWithJarOrApkExtension(TEST_FILE_NAME_WITH_CONTAINER_EXTENSION));\n    }\n\n    @Test\n    public void givenAFilePathWithAContainerExtension_whenEndsWithJarOrApkExtension_thenReturnsTrue() {\n        // WHEN + THEN\n        assertTrue(endsWithJarOrApkExtension(TEST_FILE_PATH_WITH_CONTAINER_EXTENSION));\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/PackageNameHelperTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static it.necst.grabnrun.PackageNameHelper.isAValidPackageName;\nimport static it.necst.grabnrun.PackageNameHelper.revertPackageNameToURL;\nimport static junit.framework.Assert.assertFalse;\nimport static junit.framework.Assert.assertTrue;\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.MatcherAssert.assertThat;\n\nimport org.junit.Test;\n\nimport java.net.URL;\n\npublic class PackageNameHelperTest {\n\n    private static final String TEST_INVALID_PACKAGE_NAME_STARTING_WITH_A_DOT = \".com.example\";\n    private static final String TEST_INVALID_PACKAGE_NAME_WITH_AN_EMPTY_FIELD_BETWEEN_TWO_DOTS =\n            \"com..example\";\n    private static final String TEST_INVALID_PACKAGE_NAME_ENDING_WITH_A_DOT = \"com.example.\";\n    private static final String TEST_INVALID_PACKAGE_NAME_WITH_ONLY_ONE_FIELD = \"com\";\n\n    private static final String TEST_VALID_PACKAGE_NAME_WITH_TWO_FIELDS = \"com.example\";\n    private static final String TEST_VALID_PACKAGE_NAME_WITH_FOUR_FIELDS =\n            \"com.example.polimi.application\";\n\n    private static final String TEST_EXPECTED_URL_STRING_WITH_VALID_PACKAGE_NAME_WITH_TWO_FIELDS =\n            \"https://example.com/certificate.pem\";\n    private static final String TEST_EXPECTED_URL_STRING_WITH_VALID_PACKAGE_NAME_WITH_FOUR_FIELDS =\n            \"https://example.com/polimi/application/certificate.pem\";\n\n    @Test\n    public void givenAnEmptyPackageName_whenIsAValidPackageName_thenReturnFalse() {\n        // GIVEN\n        String emptyPackageName = \"\";\n\n        // WHEN\n        boolean isAValidPackageName = isAValidPackageName(emptyPackageName);\n\n        // THEN\n        assertFalse(isAValidPackageName);\n    }\n\n    @Test\n    public void givenAPackageNameStartingWithADot_whenIsAValidPackageName_thenReturnFalse() {\n        // WHEN\n        boolean isAValidPackageName = isAValidPackageName(TEST_INVALID_PACKAGE_NAME_STARTING_WITH_A_DOT);\n\n        // THEN\n        assertFalse(isAValidPackageName);\n    }\n\n    @Test\n    public void givenAPackageNameWithAnEmptyFieldBetweenTwoDots_whenIsAValidPackageName_thenReturnFalse() {\n        // WHEN\n        boolean isAValidPackageName = isAValidPackageName(\n                TEST_INVALID_PACKAGE_NAME_WITH_AN_EMPTY_FIELD_BETWEEN_TWO_DOTS);\n\n        // THEN\n        assertFalse(isAValidPackageName);\n    }\n\n    @Test\n    public void givenAPackageNameEndingWithADot_whenIsAValidPackageName_thenReturnFalse() {\n        // WHEN\n        boolean isAValidPackageName = isAValidPackageName(TEST_INVALID_PACKAGE_NAME_ENDING_WITH_A_DOT);\n\n        // THEN\n        assertFalse(isAValidPackageName);\n    }\n\n    @Test\n    public void givenAPackageNameWithOnlyOneField_whenIsAValidPackageName_thenReturnFalse() {\n        // WHEN\n        boolean isAValidPackageName = isAValidPackageName(TEST_INVALID_PACKAGE_NAME_WITH_ONLY_ONE_FIELD);\n\n        // THEN\n        assertFalse(isAValidPackageName);\n    }\n\n    @Test\n    public void givenAPackageNameWithAtLeastTwoFieldsSeparatedByASingleDot_whenIsAValidPackageName_thenReturnTrue() {\n        // WHEN\n        boolean isAValidPackageName = isAValidPackageName(TEST_VALID_PACKAGE_NAME_WITH_TWO_FIELDS);\n\n        // THEN\n        assertTrue(isAValidPackageName);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenANotValidPackageName_whenRevertPackageNameToURL_thenThrows() throws Exception {\n        // WHEN\n        revertPackageNameToURL(TEST_INVALID_PACKAGE_NAME_WITH_ONLY_ONE_FIELD);\n    }\n\n    @Test\n    public void givenAValidPackageNameWithTwoFields_whenRevertPackageNameToURL_thenReturnsTheRevertedPackageNameAsDomainName() throws Exception {\n        // WHEN\n        URL revertPackageNameToURL = revertPackageNameToURL(TEST_VALID_PACKAGE_NAME_WITH_TWO_FIELDS);\n\n        // THEN\n        assertThat(\n                revertPackageNameToURL,\n                is(equalTo(new URL(TEST_EXPECTED_URL_STRING_WITH_VALID_PACKAGE_NAME_WITH_TWO_FIELDS))));\n    }\n\n    @Test\n    public void givenAValidPackageNameWithThreeOrMoreFields_whenRevertPackageNameToURL_thenReturnsTheRevertedTwoFieldsOfPackageNameAsDomainNameAndTheRestAsFilePart() throws Exception {\n        // WHEN\n        URL revertPackageNameToURL = revertPackageNameToURL(TEST_VALID_PACKAGE_NAME_WITH_FOUR_FIELDS);\n\n        // THEN\n        assertThat(\n                revertPackageNameToURL,\n                is(equalTo(new URL(TEST_EXPECTED_URL_STRING_WITH_VALID_PACKAGE_NAME_WITH_FOUR_FIELDS))));\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/PackageNameTrieTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.hamcrest.Matchers.equalTo;\n\nimport com.google.common.base.Optional;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.runners.MockitoJUnitRunner;\n\n@RunWith(MockitoJUnitRunner.class)\npublic class PackageNameTrieTest {\n    private static final String PREFIX_OF_TEST_PACKAGE_NAME = \"com.example\";\n    private static final String TEST_PACKAGE_NAME =\n            PREFIX_OF_TEST_PACKAGE_NAME + \".polimi.application\";\n\n    private static final String ANOTHER_TEST_PACKAGE_NAME_WITH_NO_COMMON_PREFIX =\n            \"it.test.system.application\";\n    private static final String ANOTHER_TEST_PACKAGE_NAME_WITH_A_COMMON_PREFIX =\n            PREFIX_OF_TEST_PACKAGE_NAME + \".system.myapplication\";\n\n    private PackageNameTrie testPackageNameTrie;\n\n    @Before\n    public void initializePackageNameTrie() {\n        testPackageNameTrie = new PackageNameTrie();\n    }\n\n    @Test\n    public void givenNoEntriesForPackageNameWereGenerated_whenGetPackageNameWithAssociatedCertificate_thenReturnsAnAbsentOptional() {\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // THEN\n        assertThat(optionalPackageNameWithAssociatedCertificate, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenEntriesForTestPackageNameWereGeneratedButNoPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificate_thenReturnsAnAbsentOptional() {\n        // GIVEN\n        testPackageNameTrie.generateEntriesForPackageName(TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // THEN\n        assertThat(optionalPackageNameWithAssociatedCertificate, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenNoEntriesForTestPackageNameWereGeneratedButPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificate_thenReturnsAnAbsentOptional() {\n        // GIVEN\n        testPackageNameTrie.setPackageNameHasAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // THEN\n        assertThat(optionalPackageNameWithAssociatedCertificate, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenEntriesForTestPackageNameWereGeneratedAndTestPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificate_thenReturnsAnOptionalOfTestPackageName() {\n        // GIVEN\n        testPackageNameTrie.generateEntriesForPackageName(TEST_PACKAGE_NAME);\n        testPackageNameTrie.setPackageNameHasAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // THEN\n        assertThat(\n                optionalPackageNameWithAssociatedCertificate,\n                equalTo(Optional.of(TEST_PACKAGE_NAME)));\n    }\n\n    @Test\n    public void givenEntriesForTestPackageNameWereGeneratedAndPrefixOfTestPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificateWithTestPackageName_thenReturnsAnOptionalOfPrefixOfTestPackageName() {\n        // GIVEN\n        testPackageNameTrie.generateEntriesForPackageName(TEST_PACKAGE_NAME);\n        testPackageNameTrie.setPackageNameHasAssociatedCertificate(PREFIX_OF_TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // THEN\n        assertThat(\n                optionalPackageNameWithAssociatedCertificate,\n                equalTo(Optional.of(PREFIX_OF_TEST_PACKAGE_NAME)));\n    }\n\n    @Test\n    public void givenEntriesForTestPackageNameWereGeneratedAndTestPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificateWithPrefixOfTestPackageName_thenReturnsAnAbsentOptional() {\n        // GIVEN\n        testPackageNameTrie.generateEntriesForPackageName(TEST_PACKAGE_NAME);\n        testPackageNameTrie.setPackageNameHasAssociatedCertificate(TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(PREFIX_OF_TEST_PACKAGE_NAME);\n\n        // THEN\n        assertThat(optionalPackageNameWithAssociatedCertificate, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenEntriesForTestPackageNameAndAnotherPackageNameWithNoCommonPrefixWereGeneratedAndPrefixOfTestPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificateWithTheOtherTestPackageName_thenReturnsAnAbsentOptional() {\n        // GIVEN\n        testPackageNameTrie.generateEntriesForPackageName(TEST_PACKAGE_NAME);\n        testPackageNameTrie.generateEntriesForPackageName(ANOTHER_TEST_PACKAGE_NAME_WITH_NO_COMMON_PREFIX);\n        testPackageNameTrie.setPackageNameHasAssociatedCertificate(PREFIX_OF_TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(\n                        ANOTHER_TEST_PACKAGE_NAME_WITH_NO_COMMON_PREFIX);\n\n        // THEN\n        assertThat(optionalPackageNameWithAssociatedCertificate, equalTo(Optional.<String>absent()));\n    }\n\n    @Test\n    public void givenEntriesForTestPackageNameAndAnotherPackageNameWithACommonPrefixWereGeneratedAndPrefixOfTestPackageNameWasSetToHaveACertificate_whenGetPackageNameWithAssociatedCertificateWithTheOtherTestPackageName_thenReturnsAnOptionalOfPrefixOfTestPackageName() {\n        // GIVEN\n        testPackageNameTrie.generateEntriesForPackageName(TEST_PACKAGE_NAME);\n        testPackageNameTrie.generateEntriesForPackageName(ANOTHER_TEST_PACKAGE_NAME_WITH_A_COMMON_PREFIX);\n        testPackageNameTrie.setPackageNameHasAssociatedCertificate(PREFIX_OF_TEST_PACKAGE_NAME);\n\n        // WHEN\n        final Optional<String> optionalPackageNameWithAssociatedCertificate =\n                testPackageNameTrie.getPackageNameWithAssociatedCertificate(\n                        ANOTHER_TEST_PACKAGE_NAME_WITH_A_COMMON_PREFIX);\n\n        // THEN\n        assertThat(\n                optionalPackageNameWithAssociatedCertificate,\n                equalTo(Optional.of(PREFIX_OF_TEST_PACKAGE_NAME)));\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/SecureDexClassLoaderTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static android.content.Context.CONNECTIVITY_SERVICE;\nimport static android.content.Context.MODE_PRIVATE;\nimport static it.necst.grabnrun.FileHelper.extractFileNameFromFilePath;\nimport static it.necst.grabnrun.SecureDexClassLoader.IMPORTED_CERTIFICATE_PRIVATE_DIRECTORY_NAME;\nimport static it.necst.grabnrun.SecureLoaderFactory.IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME;\nimport static it.necst.grabnrun.SecureLoaderFactory.X_509_CERTIFICATE;\nimport static junit.framework.Assert.assertNotNull;\nimport static junit.framework.Assert.assertTrue;\nimport static org.hamcrest.CoreMatchers.equalTo;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.CoreMatchers.notNullValue;\nimport static org.hamcrest.CoreMatchers.nullValue;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.mockito.Matchers.any;\nimport static org.mockito.Matchers.endsWith;\nimport static org.mockito.Matchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.ImmutableSet;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.robolectric.RobolectricGradleTestRunner;\nimport org.robolectric.annotation.Config;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.util.Collections;\nimport java.util.Map;\n\nimport dalvik.system.DexClassLoader;\nimport dalvik.system.DexFile;\nimport it.necst.grabnrun.shadows.BaseDexClassLoaderShadow;\nimport it.necst.grabnrun.shadows.DexFileShadow;\nimport it.polimi.necst.gnr.BuildConfig;\n\n@RunWith(RobolectricGradleTestRunner.class)\n@Config(constants = BuildConfig.class, shadows={BaseDexClassLoaderShadow.class, DexFileShadow.class})\npublic class SecureDexClassLoaderTest {\n\n    private static final String EMPTY_CLASS_NAME = \"\";\n    private static final String TEST_REMOTE_CONTAINER_EXTENSION = \".jar\";\n    private static final String TEST_REMOTE_CONTAINER_FILE_NAME_WITHOUT_EXTENSION = \"componentModifier\";\n    private static final String TEST_REMOTE_CONTAINER_FILE_NAME =\n            TEST_REMOTE_CONTAINER_FILE_NAME_WITHOUT_EXTENSION + TEST_REMOTE_CONTAINER_EXTENSION;\n\n    private static final String TEST_REMOTE_DOMAIN_URL = \"https://dl.dropboxusercontent.com/u/28681922/\";\n\n    private static final String TEST_REMOTE_CONTAINER_URL_AS_STRING =\n            TEST_REMOTE_DOMAIN_URL + TEST_REMOTE_CONTAINER_FILE_NAME;\n    // This container is a repack of the one above. One of its entries was modified,\n    // and now it breaks the signature verification\n    private static final String TEST_REMOTE_REPACK_CONTAINER_FILE_NAME = \"componentModifierRepack\";\n    private static final String TEST_REMOTE_REPACK_CONTAINER_URL_AS_STRING =\n            TEST_REMOTE_DOMAIN_URL + TEST_REMOTE_REPACK_CONTAINER_FILE_NAME + TEST_REMOTE_CONTAINER_EXTENSION;\n\n    private static final FileFilterByNameMatch TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER =\n            new FileFilterByNameMatch(\n                    TEST_REMOTE_CONTAINER_FILE_NAME_WITHOUT_EXTENSION, TEST_REMOTE_CONTAINER_EXTENSION);\n\n    private static final FileFilterByNameMatch TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_REPACK_CONTAINER =\n            new FileFilterByNameMatch(TEST_REMOTE_REPACK_CONTAINER_FILE_NAME, TEST_REMOTE_CONTAINER_EXTENSION);\n\n    private static final String TEST_PACKAGE_NAME = \"it.polimi.componentmodifier\";\n    private static final String TEST_A_PACKAGE_NAME_NOT_IN_THE_CONTAINER = \"com.example.application\";\n\n    private static final String TEST_CERTIFICATE_FILE_NAME = \"test_cert\";\n    private static final String TEST_CERTIFICATE_EXTENSION = \".pem\";\n    private static final String TEST_REMOTE_CERTIFICATE_URL_AS_STRING =\n            TEST_REMOTE_DOMAIN_URL + TEST_CERTIFICATE_FILE_NAME + TEST_CERTIFICATE_EXTENSION;\n    private static final String TEST_A_REMOTE_CERTIFICATE_NOT_USED_TO_SIGN_THE_CONTAINER_URL_AS_STRING =\n            TEST_REMOTE_DOMAIN_URL + \"wm\" + TEST_CERTIFICATE_EXTENSION;\n\n    // Internally certificates are renamed as the package name they are associated with..\n    private static final FileFilterByNameMatch TEST_FILE_FILTER_MATCHING_CERTIFICATE_NAMED_AS_THE_PACKAGE_NAME =\n            new FileFilterByNameMatch(TEST_PACKAGE_NAME, TEST_CERTIFICATE_EXTENSION);\n\n    private static final String TEST_CLASS_TO_LOAD =\n            \"it.polimi.componentmodifier.FirstComponentModifierImpl\";\n    // This class is not contained in the remote test container, although it has the same package name\n    private static final String TEST_A_CLASS_TO_LOAD_NOT_IN_THE_CONTAINER =\n            \"it.polimi.componentmodifier.NotPresentComponentModifierImpl\";\n\n    private static final ImmutableSet<String> TEST_SET_OF_CLASSES_IN_THE_REMOTE_CONTAINER = ImmutableSet.of(\n            \"it.polimi.componentmodifier.FirstComponentModifierImpl\",\n            \"it.polimi.componentmodifier.SecondComponentModifierImpl\",\n            \"it.polimi.componentmodifier.SecondComponentModifierImpl$1\");\n\n    private static final boolean EAGER_EVALUATION = false;\n    private static final boolean LAZY_EVALUATION = true;\n\n    @Rule public TemporaryFolder temporaryImportedContainersFolder = new TemporaryFolder();\n    @Rule public TemporaryFolder temporaryImportedCertificatesFolder = new TemporaryFolder();\n    @Rule public TemporaryFolder temporaryTrustedCertificateFolder = new TemporaryFolder();\n\n    @Mock Context mockContext = mock(Context.class);\n    @Mock ConnectivityManager mockConnectivityManager = mock(ConnectivityManager.class);\n    @Mock NetworkInfo mockNetworkInfo = mock(NetworkInfo.class);\n    @Mock PackageManager mockPackageManager = mock(PackageManager.class);\n    @Mock ContainerSignatureVerifier mockContainerSignatureVerifier =\n            mock(ContainerSignatureVerifier.class);\n\n    @Mock DexFile mockDexFile = mock(DexFile.class);\n    @Mock DexClassLoader mockDexClassLoader = mock(DexClassLoader.class);\n\n    @Before\n    public void setupMocksForSecureDexClassLoader() throws Exception {\n        when(mockContext.getSystemService(CONNECTIVITY_SERVICE))\n                .thenReturn(mockConnectivityManager);\n        when(mockConnectivityManager.getActiveNetworkInfo()).thenReturn(mockNetworkInfo);\n        when(mockNetworkInfo.isConnected()).thenReturn(true);\n\n        when(mockContext.getDir(eq(IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME), eq(MODE_PRIVATE)))\n                .thenReturn(temporaryImportedContainersFolder.getRoot());\n        when(mockContext.getDir(eq(IMPORTED_CERTIFICATE_PRIVATE_DIRECTORY_NAME), eq(MODE_PRIVATE)))\n                .thenReturn(temporaryImportedCertificatesFolder.getRoot());\n\n        DexFileShadow.setDexFileShadow(mockDexFile);\n\n        // IMPORTANT: Always create a new enumeration for this set!\n        // Otherwise, the iterator will return all the objects only for the first test case.\n        when(mockDexFile.entries()).thenReturn(\n                Collections.enumeration(TEST_SET_OF_CLASSES_IN_THE_REMOTE_CONTAINER));\n\n        when(mockContainerSignatureVerifier\n                .verifyContainerSignatureAgainstCertificate(\n                        any(String.class),\n                        any(X509Certificate.class)))\n                .thenReturn(false);\n        // IMPORTANT: Do not inline this variable!\n        // Otherwise, Mockito will complaint.\n        X509Certificate trustedCertificate =\n                retrieveAndGenerateTrustedCertificate(TEST_REMOTE_CERTIFICATE_URL_AS_STRING);\n        when(mockContainerSignatureVerifier\n                .verifyContainerSignatureAgainstCertificate(\n                        endsWith(TEST_REMOTE_CONTAINER_FILE_NAME),\n                        eq(trustedCertificate)))\n                .thenReturn(true);\n\n        when(mockDexClassLoader.loadClass(eq(TEST_A_CLASS_TO_LOAD_NOT_IN_THE_CONTAINER)))\n                .thenThrow(new ClassNotFoundException());\n        when(mockDexClassLoader.loadClass(eq(TEST_CLASS_TO_LOAD)))\n                .thenReturn((Class) \"notRelevantForTheTest\".getClass());\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateForVerification_whenLoadClassWithAnEmptyClassName_thenThrows() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(TEST_PACKAGE_NAME, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        secureDexClassLoader.loadClass(EMPTY_CLASS_NAME);\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndNoCertificateForItsPackageNameForVerification_whenLoadClassInTheContainerWithEagerEvaluation_thenRemovesRetrievedContainerAndReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_A_PACKAGE_NAME_NOT_IN_THE_CONTAINER,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThat(loadedClass, is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndNoCertificateForItsPackageNameForVerification_whenLoadClassInTheContainerWithLazyEvaluation_thenImportsRetrievedContainerButReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_A_PACKAGE_NAME_NOT_IN_THE_CONTAINER,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                LAZY_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThat(loadedClass, is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithARepackedContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadClassInTheContainerWithEagerEvaluation_thenRemovesRetrievedContainerAndReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_REPACK_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(TEST_PACKAGE_NAME, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_REPACK_CONTAINER);\n        assertThat(loadedClass, is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithARepackedContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadClassInTheContainerWithLazyEvaluation_thenRemovesRetrievedContainerAndReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_REPACK_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(TEST_PACKAGE_NAME, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                LAZY_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_REPACK_CONTAINER);\n        assertThat(loadedClass, is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateNotUsedForSigningTheContainerForItsPackageNameForVerification_whenLoadClassInTheContainerWithEagerEvaluation_thenRemovesRetrievedContainerAndReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_A_REMOTE_CERTIFICATE_NOT_USED_TO_SIGN_THE_CONTAINER_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThat(loadedClass, is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateNotUsedForSigningTheContainerForItsPackageNameForVerification_whenLoadClassInTheContainerWithLazyEvaluation_thenRemovesRetrievedContainerAndReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_A_REMOTE_CERTIFICATE_NOT_USED_TO_SIGN_THE_CONTAINER_URL_AS_STRING)),\n                LAZY_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThat(loadedClass, is(nullValue()));\n    }\n\n    @Test (expected = ClassNotFoundException.class)\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadClassNotInTheContainerWithEagerEvaluation_thenImportRetrievedContainerButThrows() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN + THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        secureDexClassLoader.loadClass(TEST_A_CLASS_TO_LOAD_NOT_IN_THE_CONTAINER);\n    }\n\n    @Test (expected = ClassNotFoundException.class)\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadClassNotInTheContainerWithLazyEvaluation_thenImportRetrievedContainerButThrows() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                LAZY_EVALUATION);\n\n        // WHEN + THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        secureDexClassLoader.loadClass(TEST_A_CLASS_TO_LOAD_NOT_IN_THE_CONTAINER);\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadClassInTheContainerWithEagerEvaluation_thenImportRetrievedContainerAndReturnsAClassInstance() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        verify(mockDexClassLoader).loadClass(eq(TEST_CLASS_TO_LOAD));\n        assertThat(loadedClass, is(notNullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadClassInTheContainerWithLazyEvaluation_thenImportRetrievedContainerAndReturnsAClassInstance() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                LAZY_EVALUATION);\n\n        // WHEN\n        Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD);\n\n        // THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        verify(mockDexClassLoader).loadClass(eq(TEST_CLASS_TO_LOAD));\n        assertThat(loadedClass, is(notNullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAnAPKContainerAndACertificateUsedToSignThatContainerForItsPackageNameForVerification_whenLoadMultipleClassesInTheContainerWhosePackageNameHasTheVerifiedOneAsPrefix_thenReturnsMultipleNotNullClassInstances() throws Exception {\n        // GIVEN\n        final String testRemoteContainerName = \"CardReaderApplication-release.apk\";\n        final String testRemoteApkContainerUrlAsString =\n                \"https://dl.dropboxusercontent.com/s/8s840gb9qhhr843/\" + testRemoteContainerName;\n        final String testRemoteCertificateUrlAsString =\n                \"https://dl.dropboxusercontent.com/s/ala85tq3ocimgi9/cardReaderTestCertificate.pem\";\n\n        final String testCommonPrefixPackageName = \"com.example.android\";\n        final String testFirstActivityClassToLoad = \"com.example.android.cardreader.MainActivity\";\n        final String testSecondActivityClassToLoad = \"com.example.android.common.activities.SampleActivityBase\";\n\n        // When reading the classes stored in the Dex file, return\n        // the set with the two classes to load\n        when(mockDexFile.entries()).thenReturn(\n                Collections.enumeration(\n                        ImmutableSet.of(testFirstActivityClassToLoad, testSecondActivityClassToLoad)));\n        when(mockDexClassLoader.loadClass(eq(testFirstActivityClassToLoad)))\n                .thenReturn((Class) \"notRelevantForTheTest\".getClass());\n        when(mockDexClassLoader.loadClass(eq(testSecondActivityClassToLoad)))\n                .thenReturn((Class) \"notRelevantForTheTest\".getClass());\n\n        // IMPORTANT: Do not inline this variable!\n        // Otherwise, Mockito will complaint.\n        X509Certificate trustedCertificate =\n                retrieveAndGenerateTrustedCertificate(testRemoteCertificateUrlAsString);\n        when(mockContainerSignatureVerifier\n                .verifyContainerSignatureAgainstCertificate(\n                        endsWith(testRemoteContainerName),\n                        eq(trustedCertificate)))\n                .thenReturn(true);\n\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        testRemoteApkContainerUrlAsString),\n                ImmutableMap.of(\n                        testCommonPrefixPackageName,\n                        new URL(testRemoteCertificateUrlAsString)),\n                LAZY_EVALUATION);\n\n        // WHEN\n        Class<?> firstLoadedActivityClass = secureDexClassLoader.loadClass(testFirstActivityClassToLoad);\n        Class<?> secondLoadedActivityClass = secureDexClassLoader.loadClass(testSecondActivityClassToLoad);\n\n        // THEN\n        verify(mockDexClassLoader).loadClass(eq(testFirstActivityClassToLoad));\n        assertThat(firstLoadedActivityClass, is(notNullValue()));\n        verify(mockDexClassLoader).loadClass(eq(testSecondActivityClassToLoad));\n        assertThat(secondLoadedActivityClass, is(notNullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateAssociatedToIt_whenWipeOutPrivateAppCachedDataForNoneContainersAndCertificates_thenAllImportedFilesAreNotErasedAndLoadClassReturnsAClass() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        secureDexClassLoader.wipeOutPrivateAppCachedData(false, false);\n\n        // THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThatOnlyOneFileIsPresentInTheImportedCertificateFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_CERTIFICATE_NAMED_AS_THE_PACKAGE_NAME);\n        assertThat(secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD), is(notNullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateAssociatedToIt_whenWipeOutPrivateAppCachedDataForContainers_thenAllImportedContainersAreErasedAndLoadClassReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        secureDexClassLoader.wipeOutPrivateAppCachedData(true, false);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThatOnlyOneFileIsPresentInTheImportedCertificateFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_CERTIFICATE_NAMED_AS_THE_PACKAGE_NAME);\n        assertThat(secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD), is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateAssociatedToIt_whenWipeOutPrivateAppCachedDataForCertificates_thenAllImportedCertificatesAreErasedAndLoadClassReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        secureDexClassLoader.wipeOutPrivateAppCachedData(false, true);\n\n        // THEN\n        assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThatNoFileIsPresentInTheImportedCertificateFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_CERTIFICATE_NAMED_AS_THE_PACKAGE_NAME);\n        assertThat(secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD), is(nullValue()));\n    }\n\n    @Test\n    public void givenASecureDexClassLoaderWithAContainerAndACertificateAssociatedToIt_whenWipeOutPrivateAppCachedDataForBothContainersAndCertificates_thenAllImportedFilesAreErasedAndLoadClassReturnsNull() throws Exception {\n        // GIVEN\n        SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues(\n                downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n                        TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                ImmutableMap.of(\n                        TEST_PACKAGE_NAME,\n                        new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)),\n                EAGER_EVALUATION);\n\n        // WHEN\n        secureDexClassLoader.wipeOutPrivateAppCachedData(true, true);\n\n        // THEN\n        assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER);\n        assertThatNoFileIsPresentInTheImportedCertificateFolderMatchingTheFilter(\n                TEST_FILE_FILTER_MATCHING_CERTIFICATE_NAMED_AS_THE_PACKAGE_NAME);\n        assertThat(secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD), is(nullValue()));\n    }\n\n    private SecureDexClassLoader initializeSecureDexClassLoaderWithTestValues(\n            String dexPathWithLocalContainers,\n            Map<String, URL> sanitizedPackageNameToCertificateURLMap,\n            boolean performLazyEvaluation) throws CertificateException {\n        return new SecureDexClassLoader(\n                dexPathWithLocalContainers,\n                mockDexClassLoader,\n                mockContext,\n                sanitizedPackageNameToCertificateURLMap,\n                performLazyEvaluation,\n                mockContainerSignatureVerifier,\n                CertificateFactory.getInstance(X_509_CERTIFICATE));\n    }\n\n    private String downloadRemoteContainerIntoTemporaryImportedContainersFolder(\n            String remoteContainerURLAsString) throws Exception {\n        return downloadRemoteResourceIntoTemporaryFolder(\n                remoteContainerURLAsString, temporaryImportedContainersFolder);\n    }\n\n    private X509Certificate retrieveAndGenerateTrustedCertificate(\n            String remoteCertificateURLAsString) throws Exception {\n        String trustedCertificatePath = downloadRemoteResourceIntoTemporaryFolder(\n                remoteCertificateURLAsString, temporaryTrustedCertificateFolder);\n\n        InputStream inStream = new FileInputStream(trustedCertificatePath);\n        X509Certificate trustedCertificate = (X509Certificate) CertificateFactory\n                .getInstance(X_509_CERTIFICATE)\n                .generateCertificate(inStream);\n\n        assertNotNull(inStream);\n        inStream.close();\n\n        return trustedCertificate;\n    }\n\n    private String downloadRemoteResourceIntoTemporaryFolder(\n            String remoteURLAsString,\n            TemporaryFolder temporaryFolder) throws Exception {\n        URL remoteURL = new URL(remoteURLAsString);\n\n        String localContainerURI = temporaryFolder.getRoot().toString()\n                + File.separator + extractFileNameFromFilePath(remoteURL.getPath());\n\n        boolean isRemoteResourceSuccessful = new FileDownloader(mockContext).downloadRemoteResource(\n                remoteURL,\n                localContainerURI,\n                false);\n\n        assertTrue(isRemoteResourceSuccessful);\n        return localContainerURI;\n    }\n\n    private void assertThatNoFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n            FileFilter testFileFilter) {\n        assertThatFileIsPresentInTheTemporaryFolderMatchingTheFilterInQuantity(\n                temporaryImportedContainersFolder, testFileFilter, 0);\n    }\n\n    private void assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter(\n            FileFilter testFileFilter) {\n        assertThatFileIsPresentInTheTemporaryFolderMatchingTheFilterInQuantity(\n                temporaryImportedContainersFolder, testFileFilter, 1);\n    }\n\n    private void assertThatNoFileIsPresentInTheImportedCertificateFolderMatchingTheFilter(\n            FileFilterByNameMatch testFileFilter) {\n        assertThatFileIsPresentInTheTemporaryFolderMatchingTheFilterInQuantity(\n                temporaryImportedCertificatesFolder, testFileFilter, 0);\n    }\n\n    private void assertThatOnlyOneFileIsPresentInTheImportedCertificateFolderMatchingTheFilter(\n            FileFilterByNameMatch testFileFilter) {\n        assertThatFileIsPresentInTheTemporaryFolderMatchingTheFilterInQuantity(\n                temporaryImportedCertificatesFolder, testFileFilter, 1);\n    }\n\n    private void assertThatFileIsPresentInTheTemporaryFolderMatchingTheFilterInQuantity(\n            TemporaryFolder temporaryFolder, FileFilter testFileFilter, int numberOfMatches) {\n        assertThat(\n                temporaryFolder\n                        .getRoot()\n                        .listFiles(testFileFilter)\n                        .length,\n                is(equalTo(numberOfMatches)));\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/SecureLoaderFactoryTest.java",
    "content": "package it.necst.grabnrun;\n\nimport static android.content.Context.CONNECTIVITY_SERVICE;\nimport static android.content.Context.MODE_PRIVATE;\nimport static it.necst.grabnrun.SecureLoaderFactory.IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME;\nimport static it.necst.grabnrun.SecureLoaderFactory.OUTPUT_DEX_CLASSES_DIRECTORY_NAME;\nimport static junit.framework.Assert.assertTrue;\nimport static org.hamcrest.CoreMatchers.is;\nimport static org.hamcrest.CoreMatchers.notNullValue;\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.mockito.Matchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.google.common.collect.ImmutableSet;\nimport com.google.common.collect.Maps;\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mock;\nimport org.robolectric.RobolectricGradleTestRunner;\nimport org.robolectric.annotation.Config;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.util.Collections;\nimport java.util.Enumeration;\nimport java.util.Map;\n\nimport dalvik.system.DexFile;\nimport it.necst.grabnrun.shadows.BaseDexClassLoaderShadow;\nimport it.necst.grabnrun.shadows.DexFileShadow;\nimport it.polimi.necst.gnr.BuildConfig;\n\n@RunWith(RobolectricGradleTestRunner.class)\n@Config(constants = BuildConfig.class, shadows={BaseDexClassLoaderShadow.class, DexFileShadow.class})\npublic class SecureLoaderFactoryTest {\n\n    private static final String EMPTY_DEX_PATH = \"\";\n    private static final String TEST_REMOTE_CONTAINER_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/u/28681922/componentModifier.jar\";\n    private static final String TEST_PACKAGE_NAME = \"com.example.application\";\n\n    private static final String TEST_REMOTE_CERTIFICATE_URL_AS_STRING =\n            \"https://dl.dropboxusercontent.com/u/28681922/test_cert.pem\";\n\n    private static final ImmutableSet<String> TEST_SET_OF_CLASSES_IN_THE_REMOTE_CONTAINER = ImmutableSet.of(\n            \"it.polimi.componentmodifier.FirstComponentModifierImpl\",\n            \"it.polimi.componentmodifier.SecondComponentModifierImpl\",\n            \"it.polimi.componentmodifier.SecondComponentModifierImpl$1\");\n\n    @Rule public TemporaryFolder temporaryImportedContainersFolder = new TemporaryFolder();\n    @Rule public TemporaryFolder temporaryOutputDexFolder = new TemporaryFolder();\n    @Rule public TemporaryFolder temporaryFolderHoldingALocalContainer = new TemporaryFolder();\n\n    @Mock Context mockContext = mock(Context.class);\n    @Mock ConnectivityManager mockConnectivityManager = mock(ConnectivityManager.class);\n    @Mock NetworkInfo mockNetworkInfo = mock(NetworkInfo.class);\n    @Mock ClassLoader mockClassLoader = mock(ClassLoader.class);\n    @Mock PackageManager mockPackageManager = mock(PackageManager.class);\n\n    @Mock DexFile mockDexFile = mock(DexFile.class);\n\n    SecureLoaderFactory testSecureLoaderFactory;\n\n    @Before\n    public void setupMocksForSecureLoaderFactory() {\n        when(mockContext.getSystemService(CONNECTIVITY_SERVICE))\n                .thenReturn(mockConnectivityManager);\n        when(mockConnectivityManager.getActiveNetworkInfo()).thenReturn(mockNetworkInfo);\n        when(mockNetworkInfo.isConnected()).thenReturn(true);\n\n        when(mockContext.getPackageManager())\n                .thenReturn(mockPackageManager);\n\n        when(mockContext.getDir(eq(IMPORTED_CONTAINERS_PRIVATE_DIRECTORY_NAME), eq(MODE_PRIVATE)))\n                .thenReturn(temporaryImportedContainersFolder.getRoot());\n        when(mockContext.getDir(eq(OUTPUT_DEX_CLASSES_DIRECTORY_NAME), eq(MODE_PRIVATE)))\n                .thenReturn(temporaryOutputDexFolder.getRoot());\n\n        testSecureLoaderFactory = new SecureLoaderFactory(mockContext);\n\n        // IMPORTANT: Always create a new enumeration for this set!\n        // Otherwise, the iterator will return all the objects only for the first test case.\n        when(mockDexFile.entries()).thenReturn(\n                Collections.enumeration(TEST_SET_OF_CLASSES_IN_THE_REMOTE_CONTAINER));\n\n        DexFileShadow.setDexFileShadow(mockDexFile);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenANonPositiveNumberOfDaysBeforeContainerCacheExpiration_whenCreateSecureLoaderFactory_thenThrows() {\n        // GIVEN\n        int notPositiveNumberOfDaysBeforeContainerCacheExpiration = 0;\n\n        // WHEN\n        new SecureLoaderFactory(mockContext, notPositiveNumberOfDaysBeforeContainerCacheExpiration);\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenAnEmptyDexPath_whenCreateDexClassLoader_thenThrows() throws Exception {\n        // WHEN\n        testSecureLoaderFactory.createDexClassLoader(\n                EMPTY_DEX_PATH,\n                null,\n                mockClassLoader,\n                ImmutableMap.of(TEST_PACKAGE_NAME, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)));\n    }\n\n    @Test (expected = IllegalArgumentException.class)\n    public void givenADexPathPointingToARemoteResourceButThePackageNameToCertificateMapIsEmpty_whenCreateDexClassLoader_thenThrows() throws Exception {\n        // GIVEN\n        ImmutableMap<String, URL> emptyPackageNameToCertificateMap = ImmutableMap.of();\n\n        // WHEN\n        testSecureLoaderFactory.createDexClassLoader(\n                TEST_REMOTE_CONTAINER_URL_AS_STRING,\n                null,\n                mockClassLoader,\n                emptyPackageNameToCertificateMap);\n    }\n\n    @Test\n    public void givenADexPathPointingToARemoteResourceAndThePackageNameToCertificateMapContainsPackageNamesAssociatedToNullValues_whenCreateDexClassLoader_thenReturnsASecureDexClassLoaderObject() throws Exception {\n        // GIVEN\n        Map<String, URL> packageNamePointingToNullCertificateMap = Maps.newHashMap();\n        packageNamePointingToNullCertificateMap.put(TEST_PACKAGE_NAME, null);\n\n        // WHEN\n        SecureDexClassLoader secureDexClassLoader = testSecureLoaderFactory.createDexClassLoader(\n                TEST_REMOTE_CONTAINER_URL_AS_STRING,\n                null,\n                mockClassLoader,\n                packageNamePointingToNullCertificateMap);\n\n        // THEN\n        assertThat(secureDexClassLoader, is(notNullValue()));\n    }\n\n    @Test\n    public void givenADexPathPointingToARemoteResourceAndThePackageNameOfTheTargetClassHasACertificate_whenCreateDexClassLoader_thenReturnsASecureDexClassLoaderObject() throws Exception {\n        // WHEN\n        SecureDexClassLoader secureDexClassLoader = testSecureLoaderFactory.createDexClassLoader(\n                TEST_REMOTE_CONTAINER_URL_AS_STRING,\n                null,\n                mockClassLoader,\n                ImmutableMap.of(TEST_PACKAGE_NAME, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)));\n\n        // THEN\n        assertThat(secureDexClassLoader, is(notNullValue()));\n    }\n\n    @Test\n    public void givenADexPathPointingToALocalResourceAndThePackageNameOfTheTargetClassHasACertificate_whenCreateDexClassLoader_thenReturnsASecureDexClassLoaderObject() throws Exception {\n        // GIVEN\n        String testRemoteContainerURI =\n                downloadRemoteContainerIntoTemporaryFolderHoldingALocalContainer();\n\n        // WHEN\n        SecureDexClassLoader secureDexClassLoader = testSecureLoaderFactory.createDexClassLoader(\n                testRemoteContainerURI,\n                null,\n                mockClassLoader,\n                ImmutableMap.of(TEST_PACKAGE_NAME, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)));\n\n        // THEN\n        assertThat(secureDexClassLoader, is(notNullValue()));\n    }\n\n    private String downloadRemoteContainerIntoTemporaryFolderHoldingALocalContainer() throws Exception {\n        String localContainerURI = temporaryFolderHoldingALocalContainer.getRoot().toString()\n                + File.separator + \"componentModifier.jar\";\n\n        boolean isRemoteResourceSuccessful = new FileDownloader(mockContext).downloadRemoteResource(\n                new URL(TEST_REMOTE_CONTAINER_URL_AS_STRING),\n                localContainerURI,\n                false);\n\n        assertTrue(isRemoteResourceSuccessful);\n        return localContainerURI;\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/shadows/BaseDexClassLoaderShadow.java",
    "content": "package it.necst.grabnrun.shadows;\n\nimport org.robolectric.annotation.Implements;\nimport org.robolectric.internal.Shadow;\n\nimport java.io.File;\n\nimport dalvik.system.BaseDexClassLoader;\n\n@Implements(BaseDexClassLoader.class)\npublic class BaseDexClassLoaderShadow extends Shadow {\n\n    private String dexPath;\n    private File optimizedDirectory;\n    private String libraryPath;\n    private ClassLoader parent;\n\n    public void __constructor__(\n            String dexPath,\n            File optimizedDirectory,\n            String libraryPath,\n            ClassLoader parent) {\n        this.dexPath = dexPath;\n        this.optimizedDirectory = optimizedDirectory;\n        this.libraryPath = libraryPath;\n        this.parent = parent;\n    }\n}\n"
  },
  {
    "path": "gnr/app/src/test/java/it/necst/grabnrun/shadows/DexFileShadow.java",
    "content": "package it.necst.grabnrun.shadows;\n\nimport org.robolectric.annotation.Implementation;\nimport org.robolectric.annotation.Implements;\n\nimport java.io.IOException;\nimport java.util.Enumeration;\n\nimport dalvik.system.DexFile;\n\n@Implements(DexFile.class)\npublic class DexFileShadow {\n    private static DexFile dexFile;\n\n    public static void setDexFileShadow(DexFile dexFile) {\n        DexFileShadow.dexFile = dexFile;\n    }\n\n    @Implementation\n    public static DexFile loadDex(String sourcePathName, String outputPathName, int flags) throws IOException {\n        return dexFile;\n    }\n\n    @Implementation\n    public Enumeration<String> entries() {\n        return dexFile.entries();\n    }\n}\n"
  },
  {
    "path": "gnr/build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.3.0'\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        mavenCentral()\n    }\n}\n"
  },
  {
    "path": "gnr/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-all.zip\n"
  },
  {
    "path": "gnr/gradle-app.setting",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<root>\n  <setting name=\"task-tab\">\n    <setting name=\"show-description\" value=\"true\"/>\n  </setting>\n  <setting name=\"favorites-tab\"/>\n  <setting name=\"command_line-tab\"/>\n  <setting name=\"setup-tab\">\n    <setting name=\"setup\">\n      <setting name=\"custom-gradle-executor\"/>\n      <setting name=\"current-directory\" value=\"/home/luca/studio-workspace/gnr\"/>\n      <setting name=\"log-level\" value=\"LIFECYCLE\"/>\n    </setting>\n  </setting>\n  <setting name=\"SinglePaneUIInstance_splitter-id\">\n    <setting name=\"divider_location\" value=\"430\"/>\n  </setting>\n  <setting name=\"main_panel\">\n    <setting name=\"current-tab\" value=\"Task Tree\"/>\n  </setting>\n  <setting name=\"Application_window-id\">\n    <setting name=\"window_x\" value=\"566\"/>\n    <setting name=\"window_y\" value=\"114\"/>\n    <setting name=\"window_width\" value=\"800\"/>\n    <setting name=\"window_height\" value=\"800\"/>\n    <setting name=\"extended-state\" value=\"0\"/>\n  </setting>\n</root>\n"
  },
  {
    "path": "gnr/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gnr/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "gnr/settings.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "grabandrun.bib",
    "content": "@InProceedings{falsina15:grabandrun,\n  author = {Luca Falsina and Yanick Fratantonio and Stefano Zanero and Christopher Kruegel and Giovanni Vigna and Federico Maggi},\n  title = {{Grab'n Run: Secure and Practical Dynamic Code Loading for Android Applications}},\n  booktitle = {Proceedings of the Annual Computer Security Applications Conference (ACSAC)},\n  month = {December},\n  year = {2015},\n  address = {Los Angeles, CA}\n}\n"
  },
  {
    "path": "repackPOC/.gitignore",
    "content": "/previousExperiments/\n/RepackInputSelector/.gitignore\n"
  },
  {
    "path": "repackPOC/RepackInputSelector/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.jdt.launching.JRE_CONTAINER\"/>\n\t<classpathentry kind=\"output\" path=\"bin\"/>\n</classpath>\n"
  },
  {
    "path": "repackPOC/RepackInputSelector/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>RepackInputSelector</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "repackPOC/RepackInputSelector/src/it/necst/grabnrun/selector/LinkContainerDialog.java",
    "content": "package it.necst.grabnrun.selector;\n\nimport java.awt.BorderLayout;\nimport java.awt.Button;\nimport java.awt.CardLayout;\nimport java.awt.Dialog;\nimport java.awt.Frame;\nimport java.awt.GridLayout;\nimport java.awt.Label;\nimport java.awt.Panel;\nimport java.awt.TextField;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.ItemEvent;\nimport java.awt.event.ItemListener;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.swing.BoxLayout;\nimport javax.swing.JCheckBox;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JSeparator;\n\npublic class LinkContainerDialog extends Dialog {\n\n\tprivate static final long serialVersionUID = 4683700510412528166L;\n\tprivate int MAX_NUMBER_OF_MAP_ENTRIES;\n\n\tprivate static final String DEFAULT_VALUE = \"default\";\n\t\n\tprivate CardLayout cardLayout;\n\tprivate Panel cardPanel, valOneCertPan, valManyCertPan, valWithAssocMapPan, comboBoxPanel;\n\n\tprivate TextField oneCertPathField, defInManyCertsField;\n\tprivate List<TextField> manyCertPathTextFieldList;\n\tprivate List<TextField> assocMapTextFieldList;\n\t\n\tprivate Button finish;\n\tprivate JComboBox<String> comboBox;\n\t\n\tprivate String apkPath;\n\tprivate Set<String> validURLContSet;\n\tprivate JCheckBox allowDefault;\n\t\n\tprivate static final String VAL_ONE_CERT = \"Validate all containers with one certificate\";\n\tprivate static final String VAL_MANY_CERTS = \"Link each container with a certificate\";\n\tprivate static final String VAL_ASS_MAP = \"Provide associative map\";\n\t\n\tpublic LinkContainerDialog(\tFrame owner, \n\t\t\t\t\t\t\t\tString title, \n\t\t\t\t\t\t\t\tString apkPath, \n\t\t\t\t\t\t\t\tSet<String> validURLContSet) {\n\t\n\t\tsuper(owner, title, true);\n\t\t\n\t\tthis.addWindowListener(new WindowAdapter() {\n\t\t\tpublic void windowClosing(final WindowEvent e) {\n                setVisible(false);\n                dispose();\n            }\n\t\t});\n\t\t\n\t\tthis.MAX_NUMBER_OF_MAP_ENTRIES = validURLContSet.size() + 3;\n\t\t\n\t\tthis.apkPath = apkPath;\n\t\tthis.validURLContSet = validURLContSet;\n\t\t\n\t\t// setSize(100, 100);\n\t\t\n\t\tcardPanel = new Panel();\n\t\tcomboBoxPanel = new Panel();\n\t\t\n\t\t// Setup cards layout\n\t\tcardLayout = new CardLayout();\n\t\tcardPanel.setLayout(cardLayout);\n\t\t\n\t\tvalOneCertPan = new Panel();\n\t\tvalManyCertPan = new Panel();\n\t\tvalWithAssocMapPan = new Panel();\n\t\t\n\t\t// Setup of the first card:\n\t\t// One certificate to validate all the containers.\n\t\tvalOneCertPan.setLayout(new BorderLayout());\n\t\tallowDefault = new JCheckBox(\"<html>Allow the tool to patch also containers not listed in the previous dialog with the same remote certificate.</html>\");\n\t\tallowDefault.setMnemonic(KeyEvent.VK_A);\n        JLabel explValOneCertLabel = new JLabel(\"<html>Here all the containers will be evaluated against a certificate located at the provided not empty remote URL.<br>Please also note that HTTPS protocol is required for this entry.</html>\", JLabel.CENTER);\n        oneCertPathField = new TextField(MainFrame.MAX_NUM_COLUMNS);\n        valOneCertPan.add(explValOneCertLabel, BorderLayout.CENTER);\n        valOneCertPan.add(allowDefault, BorderLayout.NORTH);\n        valOneCertPan.add(oneCertPathField, BorderLayout.SOUTH);\n\n        // Setup of the second card:\n     \t// Each container is linked to a certificate.\n        valManyCertPan.setLayout(new BorderLayout());\n        JLabel explValManyCertLabel = new JLabel(\"<html>Here each container must be linked with a not empty URL pointing to a remote certificate.<br>HTTPS protocol is required for all the provided entries.<br>There is also an optional final entry in case you want to validate all the other found<br>containers with a default remote certificate.</html>\", JLabel.CENTER);\n        valManyCertPan.add(explValManyCertLabel, BorderLayout.NORTH);\n        Panel contAndCertPan = new Panel();\n        contAndCertPan.setLayout(new GridLayout(0, 2));\n        contAndCertPan.add(new JLabel(\"<html>Container Remote URL / Local File Path</html>\", JLabel.CENTER));\n        contAndCertPan.add(new JLabel(\"<html>Remote certificate URL <strong>[HTTPS]</strong></html>\", JLabel.CENTER));\n        \n        // Insert two entries for each valid container URL\n        // First show the container URL, while the second asks for the certificate URL.\n        Iterator<String> validContainerURLIterator = validURLContSet.iterator();\n        manyCertPathTextFieldList = new ArrayList<TextField>();\n\t\t\n\t\twhile (validContainerURLIterator.hasNext()) {\n\t\t\t\n\t\t\t// Add the label for the container URL\n\t\t\tcontAndCertPan.add(new Label(validContainerURLIterator.next()));\n\t\t\t\n\t\t\t// Create and add the TextField for the remote certificate URL\n\t\t\tTextField oneCertPathTextField = new TextField(MainFrame.MAX_NUM_COLUMNS);\n\t\t\tmanyCertPathTextFieldList.add(oneCertPathTextField);\n\t\t\tcontAndCertPan.add(oneCertPathTextField);\n\t\t}\n\t\t\n\t\t// Finally add the default TextField in case the user wants to enable the default value..\n\t\tcontAndCertPan.add(new Label(\"Other containers [This field is OPTIONAL]\"));\n\t\tdefInManyCertsField = new TextField(MainFrame.MAX_NUM_COLUMNS);\n\t\tcontAndCertPan.add(defInManyCertsField);\n\t\t\n\t\t// Fill in the remaining entries to match the third card size\n\t\tfor (int i=0; i < MAX_NUMBER_OF_MAP_ENTRIES - validURLContSet.size() - 1; i ++) {\n\t\t\t\n\t\t\t// Fill in a line with two labels.\n\t\t\tcontAndCertPan.add(new Label());\n\t\t\tcontAndCertPan.add(new Label());\n\t\t}\n        \n        valManyCertPan.add(contAndCertPan, BorderLayout.CENTER);\n        \n        // Setup of the third card:\n     \t// Provide an associative map between package names and certificates.\n        valWithAssocMapPan.setLayout(new BorderLayout());\n        JLabel explAssMapLabel = new JLabel(\"<html>Here you can directly provide an associative map which links package names to not empty URL location pointing<br>to remote certificates (HTTPS protocol is required for all entries).<br>Refer to GNR documentation to learn how to populate this map; otherwise use one of the previous two options!</html>\", JLabel.CENTER);\n        valWithAssocMapPan.add(explAssMapLabel, BorderLayout.NORTH);\n        Panel assocMapPan = new Panel();\n        assocMapPan.setLayout(new GridLayout(0, 2));\n        assocMapPan.add(new JLabel(\"<html>Package name</html>\", JLabel.CENTER));\n        assocMapPan.add(new JLabel(\"<html>Remote certificate URL <strong>[HTTPS]</strong></html>\", JLabel.CENTER));\n        \n        // Insert two TextFields for each line.\n        // First asks for the package name, while the second for the certificate URL.\n        assocMapTextFieldList = new ArrayList<TextField>();\n\t\t\n        for (int i = 0; i < MAX_NUMBER_OF_MAP_ENTRIES; i++) {\n        \t\n        \t// Create and add the TextField for a package name entry.\n        \tTextField packageNameTextField = new TextField(MainFrame.MAX_NUM_COLUMNS);\n        \tassocMapTextFieldList.add(packageNameTextField);\n        \tassocMapPan.add(packageNameTextField);\n        \t\n        \t// Create and add the TextField for a remote certificate URL entry.\n        \tTextField oneCertPathTextField = new TextField(MainFrame.MAX_NUM_COLUMNS);\n        \tassocMapTextFieldList.add(oneCertPathTextField);\n        \tassocMapPan.add(oneCertPathTextField);\n        }\n        \n\t\tvalWithAssocMapPan.add(assocMapPan, BorderLayout.CENTER);\n        \n        // Setup of the combo box to show different cards\n        cardPanel.add(valOneCertPan, VAL_ONE_CERT);\n        cardPanel.add(valManyCertPan, VAL_MANY_CERTS);\n        cardPanel.add(valWithAssocMapPan, VAL_ASS_MAP);\n        \n        String comboBoxItems[] = { VAL_ONE_CERT, VAL_MANY_CERTS, VAL_ASS_MAP };\n        \n        comboBox = new JComboBox<String>(comboBoxItems);\n        comboBox.setEditable(false);\n        comboBox.addItemListener(new ItemListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void itemStateChanged(ItemEvent e) {\n\t\t\t\t\n\t\t\t\tCardLayout cl = (CardLayout) cardPanel.getLayout();\n\t\t\t\tcl.show(cardPanel, (String) e.getItem());\n\t\t\t}\n\t\t});\n        comboBoxPanel.add(comboBox);\n        \n        Panel centerPanel = new Panel();\n        centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));\n        centerPanel.add(new JSeparator());\n        centerPanel.add(cardPanel);\n        centerPanel.add(new JSeparator());\n        \n        // Setup of the finish button\n        finish = new Button(\"Finish\");\n        finish.addActionListener(new ActionListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\n\t\t\t\tLinkContainerDialog.this.validateInput();\n\t\t\t}\n\t\t});\n        \n        add(comboBoxPanel, BorderLayout.NORTH);\n        add(centerPanel, BorderLayout.CENTER);\n        add(finish, BorderLayout.SOUTH);\n\t\t\n\t\t// Show dialog\n        this.setResizable(false);\n\t\tthis.pack();\n\t\tthis.setLocationRelativeTo(owner);\n\t\tthis.setVisible(true);\n\t\t\n\t}\n\n\tprivate void validateInput() {\n\t\t\n\t\t// Depending on which card is currently shown,\n\t\t// the verification process changes..\n\t\tString chosenCard = (String) comboBox.getSelectedItem();\n\t\t\n\t\tMap<String, String> keyToCertificateURLMap = new HashMap<String, String> ();\n\t\t\n\t\tswitch(chosenCard) {\n\t\t\n\t\tcase VAL_ONE_CERT:\n\t\t\t\n\t\t\tString certificateURL = oneCertPathField.getText();\n\t\t\t\n\t\t\tif (isValidRemoteCertURL(certificateURL)) {\n\t\t\t\t\n\t\t\t\tIterator<String> validContainerURLIterator = validURLContSet.iterator();\n\t\t\t\t\n\t\t\t\twhile (validContainerURLIterator.hasNext()) {\n\t\t\t\t\t\n\t\t\t\t\t// Populate a map which links container URL with \n\t\t\t\t\t// the same certificate URL..\n\t\t\t\t\tkeyToCertificateURLMap.put(validContainerURLIterator.next(), certificateURL);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (allowDefault.isSelected()) {\n\t\t\t\t\t\n\t\t\t\t\t// If the user allow the tool to patch all the found containers\n\t\t\t\t\t// add a special default entry as well..\n\t\t\t\t\tkeyToCertificateURLMap.put(DEFAULT_VALUE, certificateURL);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Store all the preferences into an helper file\n\t\t\t\t// and then exit.\n\t\t\t\tsaveAndFinish(keyToCertificateURLMap, false);\n\t\t\t\t\n\t\t\t} else\n\t\t\t\tJOptionPane.showMessageDialog(this, \"The provided URL was empty or did not match a remote URL using HTTPS!\", \"Invalid certificate URL\", JOptionPane.ERROR_MESSAGE);\n\t\t\tbreak;\n\t\t\n\t\tcase VAL_MANY_CERTS:\n\t\t\t\n\t\t\tboolean isUserInputValid = true;\n\t\t\t\n\t\t\t// Check that all the entries are not empty and\n\t\t\t// valid remote URL with HTTPS protocol.\n\t\t\tfor (TextField currentTextField : manyCertPathTextFieldList) {\n\t\t\t\t\n\t\t\t\tif (!isValidRemoteCertURL(currentTextField.getText())) {\n\t\t\t\t\tisUserInputValid = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (isUserInputValid) {\n\t\t\t\t\n\t\t\t\tIterator<String> validContainerURLIterator = validURLContSet.iterator();\n\t\t\t\tint indexToPickTextField = 0;\n\t\t\t\t\n\t\t\t\twhile (validContainerURLIterator.hasNext()) {\n\t\t\t\t\t\n\t\t\t\t\t// Populate a map which links container URL with \n\t\t\t\t\t// the same certificate URL..\n\t\t\t\t\tkeyToCertificateURLMap.put(\tvalidContainerURLIterator.next(), \n\t\t\t\t\t\t\t\t\t\t\t\tmanyCertPathTextFieldList.get(indexToPickTextField).getText());\n\t\t\t\t\tindexToPickTextField++;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t// Validation of the default value, if present..\n\t\t\t\tString defaultRemoteCertURL = defInManyCertsField.getText();\n\t\t\t\t\n\t\t\t\tif (defaultRemoteCertURL.isEmpty()) {\n\t\t\t\t\t\n\t\t\t\t\t// Store all the preferences into an helper file\n\t\t\t\t\t// and then exit.\n\t\t\t\t\tsaveAndFinish(keyToCertificateURLMap, false);\n\t\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\t\n\t\t\t\t\tif (isValidRemoteCertURL(defaultRemoteCertURL)) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Add also the default entry since its certificate it's valid..\n\t\t\t\t\t\tkeyToCertificateURLMap.put(DEFAULT_VALUE, defaultRemoteCertURL);\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Store all the preferences into an helper file\n\t\t\t\t\t\t// and then exit.\n\t\t\t\t\t\tsaveAndFinish(keyToCertificateURLMap, false);\n\t\t\t\t\t\t\n\t\t\t\t\t} else {\n\t\t\t\t\t\tJOptionPane.showMessageDialog(this, \"The default entry used for not listed containers must be either empty or match a remote URL using HTTPS!\", \"Invalid certificate URL\", JOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else {\n\t\t\t\tJOptionPane.showMessageDialog(this, \"At least one of the required entries was empty or did not match a remote URL using HTTPS!\", \"Invalid certificate URL\", JOptionPane.ERROR_MESSAGE);\n\t\t\t}\n\t\t\t\n\t\t\tbreak;\n\t\t\t\n\t\tcase VAL_ASS_MAP:\n\t\t\t\n\t\t\tboolean areUserPackagesValid = true;\n\t\t\tboolean areUserCertURLsValid = true;\n\t\t\tboolean inputCheckActive = false;\n\t\t\tIterator<TextField> assocMapTextFieldIterator = assocMapTextFieldList.iterator();\n\t\t\tMap<String, String> partialAssociativeMap = new HashMap<String, String>();\n\t\t\t\n\t\t\twhile (assocMapTextFieldIterator.hasNext()) {\n\t\t\t\t\n\t\t\t\t// Retrieve elements from the table in couples..\n\t\t\t\tString currentPackageName = assocMapTextFieldIterator.next().getText();\n\t\t\t\tString currentCertURL = assocMapTextFieldIterator.next().getText();\n\t\t\t\t\n\t\t\t\tif (currentPackageName != null & !currentPackageName.isEmpty()) {\n\t\t\t\t\t\n\t\t\t\t\tinputCheckActive = true;\n\t\t\t\t\t\n\t\t\t\t\tif (isValidPackageName(currentPackageName)) {\n\t\t\t\t\t\tif (isValidRemoteCertURL(currentCertURL)) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t// Valid couple of package name and certificate.\n\t\t\t\t\t\t\t// Add this couple to the partial map\n\t\t\t\t\t\t\tpartialAssociativeMap.put(currentPackageName, currentCertURL);\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tareUserCertURLsValid = false;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t} else {\n\t\t\t\t\t\tareUserPackagesValid = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// If at least a left not null entry is present then evaluate the\n\t\t\t// correctness of the input data.\n\t\t\tif (inputCheckActive) {\n\t\t\t\t\n\t\t\t\tif (areUserPackagesValid) {\n\t\t\t\t\tif (areUserCertURLsValid) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// In this case all the entries were valid so\n\t\t\t\t\t\t// store them in the preference file.\n\t\t\t\t\t\tsaveAndFinish(partialAssociativeMap, true);\n\t\t\t\t\t\t\n\t\t\t\t\t} else\n\t\t\t\t\t\tJOptionPane.showMessageDialog(this, \"At least one of the right entries linked to a valid package name was empty or did not match a remote URL using HTTPS!\", \"Invalid certificate URL\", JOptionPane.ERROR_MESSAGE);\n\t\t\t\t\t\n\t\t\t\t} else\n\t\t\t\t\tJOptionPane.showMessageDialog(this, \"<html>At least one of the not empty left entries was not a valid package name!<br>If you are in doubts please check out the definiton of package name at this link:<br>http://grab-n-run.readthedocs.org/en/latest/tutorial.html#using-standard-dexclassloader-to-load-code-dynamically</html>\", \"Invalid package name\", JOptionPane.ERROR_MESSAGE);\n\t\t\t}\n\t\t\t\n\t\t\tbreak;\n\t\t\t\n\t\tdefault:\n\t\t\tthrow new RuntimeException(\"An invalid value was returned from the combo box\");\n\t\t}\n\t\t\n\t}\n\t\n\tprivate void saveAndFinish(Map<String, String> keyToCertificateURLMap,\n\t\t\tboolean isKeyPackageName) {\n\t\t\n\t\tFile helperFile = new File(\"./preferences\");\n\t\t\n\t\t// At first erase any copy of an older pref file, if any..\n\t\tif (helperFile.exists())\n\t\t\thelperFile.delete();\n\t\t\n\t\tBufferedWriter writer = null;\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\twriter = new BufferedWriter(new FileWriter(helperFile));\n\t\t\tString newline = System.getProperty(\"line.separator\");\n\t\t\t\n\t\t\t// First write the path to the APK to patch\n\t\t\twriter.write(apkPath + newline);\n\t\t\t\n\t\t\t// Then write whether the following keys of the map are \n\t\t\t// going to be package names or container URLs\n\t\t\twriter.write(isKeyPackageName + newline);\n\t\t\t\n\t\t\t// Finally write for each key the related URL\n\t\t\t// of the remote certificate to validate it.\n\t\t\tIterator<String> keyIterator = keyToCertificateURLMap.keySet().iterator();\n\t\t\t\n\t\t\twhile (keyIterator.hasNext()) {\n\t\t\t\t\n\t\t\t\tString keyToWrite = keyIterator.next();\n\t\t\t\twriter.write(keyToWrite + \" | \" + keyToCertificateURLMap.get(keyToWrite) + newline);\n\t\t\t}\n\t\t\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t\tfinish(false);\n\t\t} finally {\n\t\t\t\n\t\t\tif (writer != null) {\n\t\t\t\ttry {\n\t\t\t\t\twriter.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tfinish(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Everything went smoothly..\n\t\tfinish(true);\n\t\t\n\t}\n\t\n\tprivate void finish(boolean isCorrectTermination) {\n\t\t\n\t\t// Hide and release this dialog.\n\t\tsetVisible(false);\n        dispose();\n        \n        if (isCorrectTermination)\n        \tSystem.exit(0);\n        \n        // An abnormal termination occurred..\n        System.exit(1);\n\t}\n\n\tprivate boolean isValidRemoteCertURL(String candidateURLString) {\n\t\t\n\t\tif (candidateURLString == null || candidateURLString.isEmpty())\n\t\t\treturn false;\n\t\t\n\t\tURL candidateURL = null;\n\t\t\n\t\ttry {\n\t\t\t\n\t\t\tcandidateURL = new URL(candidateURLString);\n\t\t\t\n\t\t\tif (!candidateURL.getProtocol().equals(\"https\"))\n\t\t\t\tthrow new MalformedURLException();\n\t\t\t\n\t\t\t// We have a valid URL\n\t\t\treturn true;\n\t\t\t\n\t\t} catch (MalformedURLException e) {\n\t\t\t// When this exception is raised the URL is invalid..\n\t\t\treturn false;\n\t\t}\n\t}\n\t\n\tprivate boolean isValidPackageName(String candidatePackNameString) {\n\t\t\n\t\tif (candidatePackNameString == null || candidatePackNameString.isEmpty())\n\t\t\treturn false;\n\t\t\n\t\tString[] packStrings = candidatePackNameString.split(\"\\\\.\");\n\t\t\n\t\tfor (String packString : packStrings) {\n\t\t\t\n\t\t\t// Requirement: all the subfields should contain at least one char..\n\t\t\tif (packString.isEmpty())\n\t\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Package names should not be too general..\n\t\t// At least two strings dot separated.\n\t\t// Example: \"com\" is rejected, while \"com.polimi\" is not.\n\t\tif (packStrings.length < 2)\n\t\t\treturn false;\n\t\t\n\t\t// Otherwise we have a valid package name.\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "repackPOC/RepackInputSelector/src/it/necst/grabnrun/selector/MainFrame.java",
    "content": "package it.necst.grabnrun.selector;\n\nimport java.awt.BorderLayout;\nimport java.awt.Button;\nimport java.awt.Choice;\nimport java.awt.FileDialog;\nimport java.awt.Frame;\nimport java.awt.Label;\nimport java.awt.Panel;\nimport java.awt.SystemColor;\nimport java.awt.TextField;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.awt.event.ItemEvent;\nimport java.awt.event.ItemListener;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.swing.BoxLayout;\nimport javax.swing.JOptionPane;\nimport javax.swing.JSeparator;\n\npublic class MainFrame extends Frame {\n\n\tprivate static final long serialVersionUID = -3423546282965389324L;\n\n\tprivate boolean isValidApkToPatch;\n\tprivate TextField apkField;\n\tprivate Button apkButton;\n\tprivate Button quit;\n\tprivate Button next;\n\t\n\tprivate Panel contSelectPanel;\n\n\tstatic final int MAX_NUM_COLUMNS = 50;\n\tprivate static final int MAX_NUM_CONT = 7;\n\t\n\tprivate TextField[] contURLFields;\n\t\n\tprivate Set<String> validURIContSet;\n\t\n\t/**\n\t * @param args\n\t */\n\tpublic static void main(String[] args) {\n\t\t\n\t\tnew MainFrame();\n\t\t\n\t}\n\t\n\tprivate MainFrame() {\n\t\tsuper(\"Grab'n Run: Repackaging Tool\");\n\t\t\n\t\t// Initialize array for containers URL TextFields\n\t\tcontURLFields = new TextField[MAX_NUM_CONT];\n\t\t\n\t\t// Graphical setup\n\t\tthis.buildContent();\n\t\tthis.addWindowListener(new WindowAdapter() {\n\t\t\tpublic void windowClosing(final WindowEvent e) {\n                MainFrame.this.quit();\n            }\n\t\t});\n\t\t\n\t\tthis.pack();\n\t\tthis.setLocationRelativeTo(null);\n\t\tthis.setResizable(false);\n\t\tthis.setVisible(true);\n\t}\n\n\tprivate void buildContent() {\n\t\t\n\t\tthis.setBackground(SystemColor.control);\n\t\tthis.apkField = new TextField(MAX_NUM_COLUMNS);\n\t\tthis.apkField.setEditable(false);\n\t\tthis.apkButton = new Button(\"Browse..\");\n\t\tthis.isValidApkToPatch = false;\n\t\tthis.quit = new Button(\"Quit\");\n\t\tthis.next = new Button(\"Next..\");\n\t\t\n\t\tthis.quit.addActionListener(new ActionListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\n\t\t\t\tMainFrame.this.quit();\n\t\t\t}\n\t\t});\n\t\t\n\t\tthis.next.addActionListener(new ActionListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\n\t\t\t\tMainFrame.this.next();\n\t\t\t}\n\t\t});\n\t\t\n\t\tthis.apkButton.addActionListener(new ActionListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void actionPerformed(ActionEvent e) {\n\t\t\t\t\n\t\t\t\tMainFrame.this.apkChoose();\n\t\t\t}\n\t\t});\n\t\t\n\t\tPanel pageEndPanel = new Panel();\n\t\tpageEndPanel.setLayout(new BoxLayout(pageEndPanel, BoxLayout.X_AXIS));\n\t\tpageEndPanel.add(quit);\n\t\tpageEndPanel.add(next);\n\t\tthis.add(BorderLayout.PAGE_END, pageEndPanel);\n\t\t\n\t\tPanel apkTextPanel = new Panel();\n\t\tapkTextPanel.setLayout(new BoxLayout(apkTextPanel, BoxLayout.PAGE_AXIS));\n\t\tapkTextPanel.add(new Label(\"Select an APK container to patch:\"));\n\t\tapkTextPanel.add(apkField);\n\t\t\n\t\tPanel apkButtonPanel = new Panel();\n\t\tapkButtonPanel.setLayout(new BoxLayout(apkButtonPanel, BoxLayout.Y_AXIS));\n\t\tapkButtonPanel.add(new Label());\n\t\tapkButtonPanel.add(apkButton);\n\t\t\n\t\tPanel apkChoosePanel = new Panel();\n\t\tapkChoosePanel.add(BorderLayout.WEST, apkTextPanel);\n\t\tapkChoosePanel.add(BorderLayout.EAST, apkButtonPanel);\n\t\tthis.add(BorderLayout.PAGE_START, apkChoosePanel);\n\t\t\n\t\tcontSelectPanel = new Panel();\n\t\tcontSelectPanel.setLayout(new BoxLayout(contSelectPanel, BoxLayout.Y_AXIS));\n\t\tPanel contNumberPanel = new Panel();\n\t\tcontNumberPanel.setLayout(new BoxLayout(contNumberPanel, BoxLayout.X_AXIS));\n\t\tcontNumberPanel.add(new Label(\"Select the number of JAR/APK containers to use as sources: \"));\n\t\tfinal Choice numberOfCont = new Choice();\n\t\t\n\t\tfor (Integer i = 1; i <= MAX_NUM_CONT ; i ++) {\n\t\t\tnumberOfCont.add(i.toString());\n\t\t}\n\t\t\n\t\tnumberOfCont.addItemListener(new ItemListener() {\n\t\t\t\n\t\t\tpublic void itemStateChanged(ItemEvent ie) {\n\t\t\t\t\n\t\t\t\tupdateContainerBoxes(Integer.valueOf(numberOfCont.getSelectedItem()));\n\t        }\n\n\t\t});\n\t\t\n\t\tcontNumberPanel.add(numberOfCont);\n\t\tcontSelectPanel.add(new JSeparator());\n\t\tcontSelectPanel.add(contNumberPanel);\n\t\t\n\t\tcontSelectPanel.add(new Label(\"Write down for each white line below either the remote URL or\"));\n\t\tcontSelectPanel.add(new Label(\"the local path of a JAR/APK container that you intend to use as\"));\n\t\tcontSelectPanel.add(new Label(\"a possible source for classes to dynamically load at runtime.\"));\n\t\t\n\t\t// TextFields for containers URL are initialized..\n\t\tfor (int i = 0; i < MAX_NUM_CONT; i++) {\n\t\t\t\n\t\t\tcontURLFields[i] = new TextField(MAX_NUM_COLUMNS);\n\t\t\tcontURLFields[i].setVisible(true);\n\t\t\tcontSelectPanel.add(contURLFields[i]);\n\t\t}\n\t\t\n\t\tupdateContainerBoxes(Integer.valueOf(0));\n\t\t\n\t\tcontSelectPanel.add(new JSeparator());\n\t\t\n\t\tthis.add(BorderLayout.CENTER, contSelectPanel);\n\t\t//contSelectPanel.setVisible(false);\n\t\t\n\t}\n\n\tprivate void updateContainerBoxes(Integer boxToShow) {\n\t\t\n\t\tfor (int i = 0; i < MAX_NUM_CONT; i ++) {\n\t\t\t\n\t\t\tif (isValidApkToPatch) {\n\t\t\t\t\n\t\t\t\tif (i < boxToShow) {\n\t\t\t\t\t\n\t\t\t\t\t//contURLFields[i].setVisible(true);\n\t\t\t\t\tcontURLFields[i].setBackground(SystemColor.WHITE);\n\t\t\t\t\tcontURLFields[i].setEditable(true);\n\t\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\t\n\t\t\t\t\t//contURLFields[i].setVisible(false);\n\t\t\t\t\t//contURLFields[i] = new TextField(MAX_NUM_COLUMNS);\n\t\t\t\t\tcontURLFields[i].setBackground(SystemColor.control);\n\t\t\t\t\tcontURLFields[i].setEditable(false);\n\t\t\t\t\t//contURLFields[i].setVisible(true);\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\tcontURLFields[i].setBackground(SystemColor.control);\n\t\t\t\tcontURLFields[i].setEditable(false);\n\t\t\t}\n\t\t}\n\t\t\n\t\tthis.pack();\n\t\t//this.setLocationRelativeTo(null);\n\t}\n\n\tprivate void apkChoose() {\n\t\t\n\t\tfinal FileDialog fileDialog = new FileDialog(this, \"Choose an APK container..\", FileDialog.LOAD);\n\t\t// fileDialog.setFile(\"*.apk\");\n\t\tfileDialog.setAlwaysOnTop(true);\n\t\tfileDialog.setVisible(true);\n\t\t\n\t\t// Select an APK from \"Recent tabs..\" does not work.\n\t\t// This is a known issue of current JDK.. Oracle should fix it..\n\t\tif (fileDialog.getDirectory() != null && fileDialog.getFiles() != null && fileDialog.getFiles().length == 1) {\n\t\t\t\n\t\t\tString apkPath = fileDialog.getFiles()[0].getAbsolutePath();\n\t\t\tif (apkPath.toLowerCase().endsWith(\".apk\")) {\n\t\t\t\t\n\t\t\t\tthis.apkField.setBackground(SystemColor.GREEN);\n\t\t\t\tthis.apkField.setText(apkPath);\n\t\t\t\t\n\t\t\t\tcontSelectPanel.setVisible(true);\n\t\t\t\tthis.pack();\n\t\t\t\t// this.setLocationRelativeTo(null);\n\t\t\t\t\n\t\t\t\tthis.isValidApkToPatch = true;\n\t\t\t\t\n\t\t\t\tupdateContainerBoxes(1);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t\n\t\t\t\tthis.apkField.setBackground(SystemColor.control);\n\t\t\t\tthis.apkField.setText(\"Invalid file! Choose an apk container!\");\n\t\t\t\t\n\t\t\t\t//contSelectPanel.setVisible(false);\n\t\t\t\t//this.pack();\n\t\t\t\t//this.setLocationRelativeTo(null);\n\t\t\t\t\n\t\t\t\tthis.isValidApkToPatch = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void next() {\n\t\t\n\t\tboolean openDialog = false;\n\t\t\n\t\tif (isValidApkToPatch) {\n\t\t\t\n\t\t\tvalidURIContSet = new HashSet<String>();\n\t\t\t\n\t\t\tfor (TextField textField : contURLFields) {\n\t\t\t\n\t\t\t\tString containerURLString = null;\n\t\t\t\t\n\t\t\t\tif (textField.isEditable())\n\t\t\t\t\tcontainerURLString = textField.getText();\n\t\t\t\t\n\t\t\t\tif (containerURLString != null && !containerURLString.isEmpty()) {\n\t\t\t\t\t\n\t\t\t\t\t// Try to parse this container location and see if\n\t\t\t\t\t// it is a valid remote URL\n\t\t\t\t\tURL containerURL = null;\n\t\t\t\t\t\n\t\t\t\t\ttry {\n\t\t\t\t\t\t\n\t\t\t\t\t\tcontainerURL = new URL(containerURLString);\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (\tcontainerURL.getProtocol().equals(\"http\") || \n\t\t\t\t\t\t\t\tcontainerURL.getProtocol().equals(\"https\")) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tvalidURIContSet.add(containerURL.toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t} catch (MalformedURLException e) {\n\t\t\t\t\t\t\n\t\t\t\t\t\t// Not a valid remote URL.. Test if at least this string \n\t\t\t\t\t\t// matches a valid local container file on the system\n\t\t\t\t\t\tif (\tcontainerURLString.toLowerCase().endsWith(\".apk\") ||\n\t\t\t\t\t\t\t\tcontainerURLString.toLowerCase().endsWith(\".jar\")) {\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tFile testForAFile = new File(containerURLString);\n\t\t\t\t\t\t\tif (testForAFile.exists() && testForAFile.isFile())\n\t\t\t\t\t\t\t\tvalidURIContSet.add(containerURLString);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif (!validURIContSet.isEmpty())\n\t\t\t\topenDialog = true;\n\t\t\t\n\t\t\tif (openDialog) {\n\t\t\t\t\n\t\t\t\tnew LinkContainerDialog(this, \"Select validation method..\", apkField.getText(), validURIContSet);\n\t\t\t\t//System.out.println(\"Found \" + validURLContSet.size() + \" valid distinct container URL.\");\n\t\t\t} else {\n\t\t\t\t\n\t\t\t\tJOptionPane.showMessageDialog(this, \"Insert at least one valid remote URL or local file path pointing to an APK/JAR container in one of the white enabled fields!\", \"No valid container remote URL or local path\", JOptionPane.ERROR_MESSAGE);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void quit() {\n        this.setVisible(false);\n        System.exit(1);\n    }\n}\n"
  },
  {
    "path": "repackPOC/repackagingTool.py",
    "content": "import sys, argparse, shutil, os\n\n# This variable matches the local folder in the repo \n# where the submodule of Androguard is located\nandroguard_location = 'androguard/' \nsys.path.insert(1, androguard_location)\n\n# Modules imported from Androguard.\nimport androguard.core.bytecodes.jvm as jvm\nimport androguard.core.bytecodes.dvm as dvm\nimport androguard.core.bytecodes.apk as apk\nimport androguard.core.analysis.analysis as analysis\n\n# Library used for download of remote container\nimport urllib, urlparse, os.path\n\n# Libary used for digest computation\nimport hashlib, base64, zipfile\n\n# Library used to create grammars and parse files\nfrom pyparsing import *\n\n# Library used to launch and check results on command line\nimport subprocess\n\n# Library used to parse the Android Manifest.\nimport xml.etree.ElementTree as ET\n\n# Androguard script which automatizes collection of useful information\n# on JAR and APK.\nimport androlyze\n\n# Grammar elements for smali code (Used for smali parsing)\nINTEGER = Word( nums, max = 1 )\nALPHABET = alphanums + '_-'\nCLASS_PATH = Combine( Word( alphas ) + ZeroOrMore( \"/\" + Word( ALPHABET )) + ZeroOrMore( OneOrMore(\"$\") + Word( ALPHABET ) ) )\nCLASS_STRING = Group(\"L\" + CLASS_PATH + \";\")\nRETURN_TYPE = Word( alphas, max = 1 )\nOPERAND = Or( RETURN_TYPE + CLASS_STRING )\nMETHOD_NAME = Or( \"<init>\" + Word( alphas ))\nMETHOD_DECL = Group(METHOD_NAME + \"(\" + ZeroOrMore(OPERAND) + \")\" + OPERAND)\nNUMBER_EXA = Group(\"(\" + Combine( \"0x\" + Word( \"0123456789abcdef\" )) + \")\")\nACCESS_ATTR = Optional(oneOf(\"private protected public synthetic\"))\nRESERVED_KEYWORDS = ZeroOrMore(oneOf(\"final interface abstract enum annotation synthetic\"))\nVAR = Combine( Word('pv', max = 1) + Word( nums ) ) \n\n# Required standard permission\nrequired_permissions = ['android.permission.ACCESS_NETWORK_STATE', 'android.permission.INTERNET', 'android.permission.READ_EXTERNAL_STORAGE']\n\n# Output code for subprogramms\nSUCCESS = 0\nFAILURE = 1\n\n# Apktool lib path\nAPKTOOL_JAR = \"libs\" + os.sep + \"apktool.jar\"\n\ndef isAValidContainer(containerPath, onlyAPKallowed = False):\n\n\tif containerPath:\n\n\t\tif os.path.exists(containerPath) and os.path.isfile(containerPath):\n\t\t\t# Try to open this as an APK container..\n\t\t\ttry:\n\t\t\t\ta = apk.APK(containerPath)\n\n\t\t\t\tif a.is_valid_APK():\n\t\t\t\t\treturn True\n\t\t\texcept zipfile.BadZipfile:\n\t\t\t\t# This is not a valid APK container..\n\t\t\t\tpass\n\n\t\t\tif not onlyAPKallowed:\n\t\t\t\t# Try to open this as a JAR container..\n\t\t\t\ttry:\n\t\t\t\t\tj = jvm.JAR(containerPath)\n\n\t\t\t\t\t# Check that in this JAR there is the\n\t\t\t\t\t# required 'classes.dex' entry.\n\t\t\t\t\tif 'classes.dex' in j.zip.namelist():\n\t\t\t\t\t\treturn True\n\n\t\t\t\texcept zipfile.BadZipfile:\n\t\t\t\t\t# This is not even a valid JAR container..\n\t\t\t\t\tpass\n\n\t# If the file reaches this branch, it means that \n\t# this is not a valid container\n\treturn False\n\ndef isARemoteURL(candidateRemoteURL, onlyHTTPSrequired = False):\n\n\t# Parse and check this remote URL\n\tparsePath = urlparse.urlparse(candidateRemoteURL)\n\n\tif onlyHTTPSrequired:\n\t\tif parsePath.scheme == \"https\":\n\t\t\treturn True\n\telse:\n\t\tif parsePath.scheme == \"http\" or parsePath.scheme == \"https\":\n\t\t\treturn True\n\n\treturn False\n\ndef isAValidPackageName(candidatePackageName):\n\n\tif candidatePackageName:\n\n\t\tif len(candidatePackageName.split('.')) < 2:\n\t\t\t# Too general package name: at least two fields dot separated..\n\t\t\treturn False\n\n\t\t# Each subfield must be not empty..\n\t\tfor subString in candidatePackageName.split('.'):\n\t\t\tif not subString:\n\t\t\t\treturn False\n\n\t\t# If previous conditions are valid than the package name is OK.\n\t\treturn True\n\n\treturn False\n\ndef downloadRemoteContainer(containerRemoteURL):\n\n\tif containerRemoteURL:\n\t\t# Check this remote URL\n\t\tif isARemoteURL(containerRemoteURL):\n\n\t\t\t# Download the remote file and store it in the current directory\n\t\t\tdownloadResult = urllib.urlretrieve(containerRemoteURL, os.path.basename(containerRemoteURL))\n\n\t\t\tif isAValidContainer(downloadResult[0]):\n\n\t\t\t\t# Return final local path of the valid container\n\t\t\t\tprint \"[In progress] Found a valid container at \" + containerRemoteURL\n\t\t\t\treturn downloadResult[0]\n\n\t\t\telse:\n\n\t\t\t\t# Remove this resource and skip it\n\t\t\t\tprint \"[Warning] Found an invalid container at \" + containerRemoteURL + \". This resource will be skipped!\"\n\t\t\t\tos.remove(downloadResult[0])\n\t\t\t\treturn None\n\n\t# If any of the previous steps fail, return None\n\tprint \"[Warning] Impossible to retrieve a resource at \" + containerRemoteURL\n\treturn None\n\ndef extractPackageNamesFromLocalContainer(containerPath):\n\n\tif containerPath:\n\n\t\tif os.path.exists(containerPath) and os.path.isfile(containerPath):\n\n\t\t\t# Initialize package name list\n\t\t\tpackageNamesList = list()\n\n\t\t\tif isAValidContainer(containerPath):\n\n\t\t\t\ta = apk.APK(containerPath)\n\t\n\t\t\t\tif a.is_valid_APK():\n\t\t\t\t\t# In case of an APK it is pretty easy to extract the app package name\n\t\t\t\t\tpackageNamesList.append(str(a.get_package()))\n\t\t\t\t\treturn packageNamesList\n\n\t\t\t\telse:\n\t\t\t\t\t# In case of a JAR at first retrieve the dex translation of the classes\n\t\t\t\t\td = dvm.DalvikVMFormat(a.get_dex())\n\n\t\t\t\t\t# Then extract the list of the full class names\n\t\t\t\t\tclassNamesList = d.get_classes_names()\n\n\t\t\t\t\tfor fullClassName in classNamesList:\n\n\t\t\t\t\t\t# Retrieve simple class name and package name\n\t\t\t\t\t\tsimpleClassName = os.path.basename(fullClassName)\n\t\t\t\t\t\tpackageName = fullClassName.lstrip(\"L\").replace(\"/\", \".\").rstrip(simpleClassName).rstrip(\".\")\n\t\t\t\t\n\t\t\t\t\t\t# Filter out all packages which comes from standard Android libraries\n\t\t\t\t\t\tif not(packageName.startswith(\"android\")) and len(packageName.split(\".\")) > 1:\n\t\t\t\t\t\t\tpackageNamesList.append(packageName)\n\n\t\t\t\t\t# Remove duplicates from this set and order it in a list\n\t\t\t\t\tpackageNamesList = list(sorted(set(packageNamesList)))\n\n\t\t\t\t\t# Analyze couples of adjacent package names\n\t\t\t\t\tcounter = 0\n\n\t\t\t\t\twhile (counter < len(packageNamesList) - 1):\n\n\t\t\t\t\t\tcouple = list([packageNamesList[counter], packageNamesList[counter + 1]])\n\t\t\t\t\t\tcommon = os.path.commonprefix(couple)\n\n\t\t\t\t\t\tif not(common) or len(common.split(\".\")) <= 1:\n\t\t\t\t\t\t\t# There is no common path among the two packages\n\t\t\t\t\t\t\t# so the first one is valid\n\t\t\t\t\t\t\tcounter = counter + 1\n\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t# There is a common prefix so only the shortest among the \n\t\t\t\t\t\t\t# two packages should be kept\n\t\t\t\t\t\t\t# Could also simply be:\n\t\t\t\t\t\t\t# del packageNamesList[counter + 1]\n\t\t\t\t\t\t\t# since following strings are always longer in this scenario.. \n\t\t\t\t\t\t\tif (len(packageNamesList[counter]) > len(packageNamesList[counter + 1])):\n\t\t\t\t\t\t\t\tdel packageNamesList[counter]\n\t\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t\tdel packageNamesList[counter + 1]\n\n\t\t\t\t\t# Finally the returned the purged and final package list\n\t\t\t\t\treturn packageNamesList\n\n\t# If this branch is reached than the package extraction failed\n\tprint \"[Warning] Impossible to extract package names for \" + containerPath\n\treturn None\n\ndef computeDigestEncode(filePath):\n\n\tif filePath:\n\n\t\tif os.path.exists(filePath) and os.path.isfile(filePath):\n\t\t\t\t# Initialize sha1 hash object\n\t\t\t\tsha1 = hashlib.sha1()\n\n\t\t\t\t# Try to open local file in read byte mode\n\t\t\t\twith open(filePath, 'rb') as fileToDigest:\n\n\t\t\t\t\t# Insert the byte of the file to digest\n\t\t\t\t\tsha1.update(fileToDigest.read())\n\n\t\t\t\t\t# Recover the base64 safe URL encode of the\n\t\t\t\t\t# digested file\n\t\t\t\t\treturn base64.urlsafe_b64encode(sha1.digest())\n\n\tprint \"[Warning] Impossible to compute digest for \" + filePath\n\treturn None\n\ndef decodeTargetAPK(apkPath):\n\n\tif apkPath:\n\n\t\tprint \"[In progress] Decode target APK..\"\n\t\tdecodeAPK = subprocess.call([\"java\",\"-jar\", APKTOOL_JAR, \"d\", \"-f\", apkPath])\n\n\t\tdecodeDirName = os.path.splitext(os.path.basename(apkPath))[0]\n\n\t\tif (decodeAPK == SUCCESS):\n\n\t\t\t# Check that the actual directory exists after this operation\n\t\t\tif (os.path.isdir(decodeDirName)):\n\n\t\t\t\t# In this case the operation was successful so return the directory name\n\t\t\t\tprint \"[In progress] APK decoding was successful.\"\n\t\t\t\treturn decodeDirName\n\n\t\t# Here the decode of the APK fails so remove any partial files generated\n\t\t# during the APK decoding.\n\t\tshutil.rmtree(decodeDirName)\n\t\t#subprocess.call([\"rm\", \"-rf\", decodeDirName])\n\n\tprint \"[Exit] An error occured while decoding target APK.\"\n\tsys.exit(FAILURE)\n\ndef buildRepackagedAPK(decodeDirName):\n\n\tif decodeDirName and os.path.isdir(decodeDirName):\n\n\t\tprint \"[In progress] Rebuilding patched APK..\"\n\t\trebuildAPKcommand = subprocess.call([\"java\",\"-jar\", APKTOOL_JAR, \"b\", decodeDirName])\n\n\t\tif (rebuildAPKcommand == SUCCESS):\n\n\t\t\t# An APK is expected now in dist folder\n\t\t\trebuiltAPKpath = decodeDirName + os.sep + \"dist\" + os.sep + decodeDirName + \".apk\"\n\t\t\t\n\t\t\tif os.path.isfile(rebuiltAPKpath):\n\n\t\t\t\t# In this case the operation was successful so return the built APK path\n\t\t\t\tprint \"[In progress] APK rebuilding was successful.\"\n\t\t\t\treturn rebuiltAPKpath\n\n\tprint \"[Exit] An error occured while rebuilding APK from patched resources.\"\n\tsys.exit(FAILURE)\n\ndef performAnalysis(apkPath):\n\n\t# Perform the analysis by recalling public method of androlyze.py\n\ta, d, dx = androlyze.AnalyzeAPK(apkPath)\n\n\tif not a.is_valid_APK():\n\t\tprint \"[Exit] The selected resource is not a valid APK!\"\n\t\treturn sys.exit(FAILURE)\n\n\tif not analysis.is_dyn_code(dx):\n\t\tprint \"[Exit] No DexClassLoader use in this APK and so there is nothing to patch!\"\n\t\treturn sys.exit(SUCCESS)\n\n\tprint \"[In progress] Analyze target APK..\"\n\n\t# Store app permissions (used later on while patching Android Manifest)\n\tapp_permissions = set(a.get_permissions())\n\n\t#app_permissions.add('android.permission.ACCESS_NETWORK_STATE')\n\t#app_permissions.add('android.permission.BLABLABLA')\n\n\t# Reference to global variable\n\tmissing_permissions = list()\n\n\tfor current_perm in required_permissions:\n\t\tif not(current_perm in app_permissions):\n\t\t\tmissing_permissions.append(current_perm)\n\n\t#print missing_permissions\n\n\t# Flag variable for later use\n\tdynamicCallsWhereTraced = False\n\n\t# Save a reference for standard output\n\tstdout = sys.stdout\n\n\t# Redirect output to an helper variable\n\t# sys.stdout = open('./dynamicCalls', 'w')\n\tdynamicCallsFilePath = os.curdir + os.sep + \"dynamicCalls\"\n\n\twith open(dynamicCallsFilePath, 'w') as dynamicCallsFile:\n\t\t# Redirect output to an helper variable\n\t\tsys.stdout = dynamicCallsFile\n\n\t\t# Highlight dynamic calls linked to a DexClassLoader.\n\t\tanalysis.show_DynCode(dx)\n\n\t\t# Set back usual stdout\n\t\tsys.stdout = stdout\n\n\twith open(dynamicCallsFilePath, 'r') as dynamicCallsFile:\n\n\t\t#print INTEGER.parseString(\"1\")\n\t\t#print CLASS_STRING.parseString(\"Lcom/example/extractapp/MainActivity;\")\n\t\t#print METHOD_DECL.parseString(\"setUpNormal()V\")\n\t\t#print NUMBER_EXA.parseString(\"(0x66)\")\n\t\t#print CLASS_STRING.parseString(\"Ldalvik/system/DexClassLoader;\")\n\n\t\t# Define a grammar to parse line of the input file.\n\t\tparsing_format = INTEGER + CLASS_STRING + \"->\" + METHOD_DECL + NUMBER_EXA + \"--->\" + CLASS_STRING + \"->\" + METHOD_DECL\n\n\t\t# Reference to global variable\n\t\tclassesWithDynCodeLoad = list()\n\n\t\tfor line in dynamicCallsFile:\n\t\t\t# print line\n\t\t\ttokens = parsing_format.parseString( line )\n\n\t\t\t# This is a sort specific parsing constant(magic numbers)..\n\t\t\tclassName = tokens[1][1]\n\n\t\t\t# Extract only name of classes to patch which are not the ones of Grab'n Run\n\t\t\tif className != \"it/necst/grabnrun/SecureDexClassLoader\":\n\t\t\t\tclassesWithDynCodeLoad.append(className)\n\n\t\t# In the end remove duplicates from this list (use a set)\n\t\tclassesWithDynCodeLoad = set(classesWithDynCodeLoad)\n\n\t\t# print classesWithDynCodeLoad\n\n\t\t# Raise flag variable\n\t\tprint \"[In progress] Dynamic calls have been detected..\"\n\t\tdynamicCallsWhereTraced = True\n\n\t# Now the helper file should be closed and erased.\n\tos.remove(dynamicCallsFilePath)\n\n\tif dynamicCallsWhereTraced:\n\t\t# This APK should be patched!\n\t\treturn missing_permissions, classesWithDynCodeLoad\n\n\t# Something went wrong..\n\tprint \"[Exit] Dynamic calls have not been detected!\"\n\treturn sys.exit(FAILURE)\n\ndef addMissingPermsToAndroidManifest(decodeDirName, missingPerms):\n\t\n\tif not decodeDirName or not(os.path.isdir(decodeDirName)):\n\t\tprint \"[Exit] Invalid folder was provided!\"\n\t\tsys.exit(FAILURE)\n\n\tprint \"[In progress] Adding missing permission to AndroidManifest.xml..\"\n\n\tandroidManifestPath = decodeDirName + os.sep + \"AndroidManifest.xml\"\n\n\tif not os.path.exists(androidManifestPath):\n\t\tprint \"[Exit] Decoded APK does not have an AndroidManifest.xml entry!\"\n\t\tsys.exit(FAILURE)\n\n\t# Generate an XML tree from the Manifest\n\ttree = ET.parse(androidManifestPath)\n\t# Get the root entry which is manifest tag\n\troot = tree.getroot()\n\t# Add the scheme attibute for namespace\n\troot.set('xmlns:android', 'http://schemas.android.com/apk/res/android')\n\n\t# For each permission add an entry to the manifest\n\tfor perm in missingPerms:\n\n\t\t# Setup a new Element under the root\n\t\tnewPermNode = ET.Element('uses-permission')\n\t\t# And fix the required permission as a property\n\t\tnewPermNode.set('android:name', perm)\n\t\t# Append this node to the root element\n\t\troot.insert(0, newPermNode)\n\n\t# Finally write the modifications on the original manifest\n\ttree.write(androidManifestPath, encoding='utf-8')\n\ndef addBlankPadding(line):\n\n\tpadding = ''\n\tblanks = len(line) - len(line.lstrip())\n\n\tpadding = padding + blanks * ' '\n\n\treturn padding\n\ndef patchSmaliClasses(decodeDirName, classesWithDynCodeLoad):\n\n\tif not decodeDirName or not(os.path.isdir(decodeDirName)):\n\t\tprint \"[Exit] Invalid folder was provided!\"\n\t\tsys.exit(FAILURE)\n\n\tprint \"[In progress] Patching .smali classes..\"\n\n\tsmaliFolder = decodeDirName + os.sep + \"smali\"\n\n\tfirstLineFormat = \".class\" + ACCESS_ATTR + RESERVED_KEYWORDS + CLASS_STRING\n\trepackHandler = \"Lit/necst/grabnrun/RepackHandler;\"\n\tlabelNumber = 0\n\n\t# Analyze all the elements inside the smali folder incrementally.. \n\tfor root, dirs, files in os.walk(smaliFolder):\n\t\t\n\t\tif root == smaliFolder:\n\t\t\t# Remove from analysis smali classes in \"android\" folder\n\t\t\tindex = 0\n\t\t\twhile(index < len(dirs)):\n\t\t\t\tif dirs[index] == 'android':\n\t\t\t\t\tdel dirs[index]\n\t\t\t\t\tbreak\n\t\t\t\tindex = index + 1\n\n\t\tfor smaliFile in files:\n\n\t\t\tsmaliFilePath = os.path.join(root, smaliFile)\n\n\t\t\t# A couple of flags to see if the target file needs\n\t\t\t# to be patched\n\t\t\tmayBeAnActivity = False\n\t\t\tcontainDynCode = False\n\n\t\t\tsmaliClassName = ''\n\t\t\tsuperClassName = ''\n\n\t\t\twith open(smaliFilePath, 'r') as smaliFileDesc:\n\t\t\t\t\n\t\t\t\t# Extract class name from first line of the smali file\n\t\t\t\t# and check whether this is in the classes set\n\t\t\t\tfirstLine = smaliFileDesc.readline()\n\t\t\t\t#print firstLine\n\t\t\t\ttokens = firstLineFormat.parseString( firstLine )\n\t\t\t\t#print tokens\n\t\t\t\tsmaliClassName = tokens[len(tokens) - 1][1]\n\t\t\t\tif smaliClassName in classesWithDynCodeLoad:\n\t\t\t\t\tcontainDynCode = True\n\n\t\t\t\t# Extract the superclass name and if it ends with Activity then mark\n\t\t\t\t# this class as a possible activity [Heuristic!!!]\n\t\t\t\tsecondLine = smaliFileDesc.readline()\n\t\t\t\tsuperClassName = secondLine.lstrip(\".super L\").rstrip().rstrip(\";\")\n\t\t\t\t# print secondLine\n\t\t\t\tif superClassName.endswith(\"Activity\"):\n\t\t\t\t\tmayBeAnActivity = True\n\n\t\t\t# Check whether one of the two flags was raised..\n\t\t\tif mayBeAnActivity or containDynCode:\n\n\t\t\t\t# This smali file needs to be patched\n\t\t\t\tprint \"[In progress] Patching \" + smaliFilePath + \"..\"\n\n\t\t\t\tfileName, extension = os.path.splitext(smaliFilePath)\n\t\t\t\tsmaliPatchedFilePath = fileName + \"Patch\" + extension\n\n\t\t\t\twith open(smaliFilePath, 'r') as original:\n\t\t\t\t\twith open(smaliPatchedFilePath, 'w') as patched:\n\n\t\t\t\t\t\t# Different grammars for the relevant lines to patch\n\t\t\t\t\t\tonCreateFormat = \"invoke-super {\" + VAR + \",\" + VAR + \"},\" + \"L\" + superClassName + \";->onCreate(Landroid/os/Bundle;)V\"\n\t\t\t\t\t\tnewInstanceDexClassLoader = \"new-instance\" + VAR + \", Ldalvik/system/DexClassLoader;\"\n\t\t\t\t\t\tinitDexClassLoader = \"invoke-direct {\" + delimitedList(VAR, delim = ',') + \"}, Ldalvik/system/DexClassLoader;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V\"\n\t\t\t\t\t\tloadClass = \"invoke-virtual {\" + VAR + \",\" + VAR + \"}, Ldalvik/system/DexClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;\"\n\t\t\t\t\t\tmovResAfterLoad = \"move-result-object\" + VAR\n\n\t\t\t\t\t\t# Flag variable..\n\t\t\t\t\t\tafterDynLoadInstruction = False\n\n\t\t\t\t\t\t# Read each line in the original file and if necessary\n\t\t\t\t\t\t# write changes in the patched version..\n\t\t\t\t\t\tfor line in original:\n\n\t\t\t\t\t\t\tif afterDynLoadInstruction:\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif line.strip():\n\t\t\t\t\t\t\t\t\tafterDynLoadInstruction = False\n\t\t\t\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\t\t\t\t# Read and patch also next line since the move result \n\t\t\t\t\t\t\t\t\t\t# must be integrated with a check on not null values \n\t\t\t\t\t\t\t\t\t\ttokens = movResAfterLoad.parseString(line)\n\t\t\t\t\t\t\t\t\t\tpatched.write(line + \"\\n\")\n\t\t\t\t\t\t\t\t\t\tsecGNRlabel = \":sec_checkgnr_\" + str(labelNumber)\n\t\t\t\t\t\t\t\t\t\tlabelNumber = labelNumber + 1\n\t\t\t\t\t\t\t\t\t\t# Write down sanity check on the final object..\n\t\t\t\t\t\t\t\t\t\tpadding = addBlankPadding(line)\n\t\t\t\t\t\t\t\t\t\tpatched.write(padding + \"if-nez \" + tokens[1] + \", \" + secGNRlabel + \"\\n\")\n\t\t\t\t\t\t\t\t\t\tpatched.write(padding + \"invoke-static {}, \" + repackHandler + \"->raiseSecurityException()V\" + \"\\n\")\n\t\t\t\t\t\t\t\t\t\tpatched.write(padding + secGNRlabel + \"\\n\")\n\t\t\t\t\t\t\t\t\t\tprint \"Added sanity check\"\n\t\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\t\texcept ParseException:\n\t\t\t\t\t\t\t\t\t\tprint \"[Warning] A dynamically loaded instance was not assigned to any variable!\"\n\t\t\t\t\t\t\t\t\t\t#sys.exit(FAILURE)\n\n\t\t\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t\t\tpatched.write(line)\n\n\t\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\t\ttokens = onCreateFormat.parseString(line)\n\t\t\t\t\t\t\t\tpatched.write(line + \"\\n\")\n\t\t\t\t\t\t\t\tonCreatePatch = addBlankPadding(line) + \"invoke-static {\" + tokens[1] + \"}, \" + repackHandler + \"->enqueRunningActivity(Landroid/app/Activity;)V\" + \"\\n\"\n\t\t\t\t\t\t\t\tpatched.write(onCreatePatch)\n\t\t\t\t\t\t\t\tprint onCreatePatch\n\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\texcept ParseException:\n\t\t\t\t\t\t\t\tpass\n\n\t\t\t\t\t\t\tif 'Ldalvik/system/DexClassLoader;' in line:\n\n\t\t\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\t\t\ttokens = newInstanceDexClassLoader.parseString(line)\n\t\t\t\t\t\t\t\t\t# If no exception is raised do not write anything back\n\t\t\t\t\t\t\t\t\t# since this line should be erased\n\t\t\t\t\t\t\t\t\tprint \"Line removed\"\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\texcept ParseException:\n\t\t\t\t\t\t\t\t\tpass\n\n\t\t\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\t\t\ttokens = initDexClassLoader.parseString(line)\n\t\t\t\t\t\t\t\t\tif len(tokens) != 7:\n\t\t\t\t\t\t\t\t\t\tprint \"[Exit] Unexpected line while patching smali file!\"\n\t\t\t\t\t\t\t\t\t\tsys.exit(FAILURE)\n\n\t\t\t\t\t\t\t\t\tinitSecDex = addBlankPadding(line) + \"invoke-static {\" + tokens[2] + \", \" + tokens[3] + \", \" + tokens[4] + \", \" + tokens[5] + \"}, \" + repackHandler + \"->generateSecureDexClassLoader(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Lit/necst/grabnrun/SecureDexClassLoader;\" + \"\\n\"\n\t\t\t\t\t\t\t\t\tmoveRes = addBlankPadding(line) + \"move-result-object \" + tokens[1] + \"\\n\"\n\t\t\t\t\t\t\t\t\tpatched.write(initSecDex + \"\\n\")\n\t\t\t\t\t\t\t\t\tpatched.write(moveRes)\n\t\t\t\t\t\t\t\t\tprint initSecDex\n\t\t\t\t\t\t\t\t\tprint moveRes\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\texcept ParseException:\n\t\t\t\t\t\t\t\t\tpass\n\n\t\t\t\t\t\t\t\ttry:\n\t\t\t\t\t\t\t\t\tloadClass.parseString(line)\n\t\t\t\t\t\t\t\t\tloadSecLine = line.replace('Ldalvik/system/DexClassLoader;', 'Lit/necst/grabnrun/SecureDexClassLoader;')\n\t\t\t\t\t\t\t\t\tpatched.write(loadSecLine)\n\t\t\t\t\t\t\t\t\t# Inform that the next instruction must be a move-result-object..\n\t\t\t\t\t\t\t\t\tafterDynLoadInstruction = True\n\t\t\t\t\t\t\t\t\tprint loadSecLine\n\t\t\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t\t\texcept ParseException:\n\t\t\t\t\t\t\t\t\tpass\n\n\t\t\t\t\t\t\t\t# All the particular cases have been handled. If the line reaches this part of the code,\n\t\t\t\t\t\t\t\t# simply replace standard DexClassLoader string..\n\t\t\t\t\t\t\t\tpatchedLine = line.replace('Ldalvik/system/DexClassLoader;', 'Lit/necst/grabnrun/SecureDexClassLoader;')\n\t\t\t\t\t\t\t\tpatched.write(patchedLine)\n\t\t\t\t\t\t\t\tprint patchedLine\n\n\t\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t\t# Those lines which reach this flow are the ones that do not match any relevant case.\n\t\t\t\t\t\t\t\t# Simply copy them in the patched file as they were in the original version.\n\t\t\t\t\t\t\t\tpatched.write(line)\n\n\t\t\t\t\t\t# Here the process of patching the smali file is finished\n\t\t\t\t\t\tprint \"[In progress] \" + smaliFilePath + \" has been patched..\"\t\t\t\t\t\t\n\n\t\t\t\t# Erase the old copy and rename the patched version to the original name\n\t\t\t\tos.remove(smaliFilePath)\n\t\t\t\tos.rename(smaliPatchedFilePath, smaliFilePath)\n\n\ndef setUpRepackHandler(decodeDirName, hasStaticAssociativeMap, entriesDictionary):\n\n\tif not decodeDirName or not(os.path.isdir(decodeDirName)):\n\t\tprint \"[Exit] Invalid folder was provided!\"\n\t\tsys.exit(FAILURE)\n\n\tprint \"[In progress] Copying Grab'n Run .smali classes\"\n\tpathGNRparentFolder = decodeDirName + os.sep + \"smali\" + os.sep + \"it\" + os.sep + \"necst\"\n\tpathGNRfolder = pathGNRparentFolder + os.sep + \"grabnrun\"\n\n\t# Create parent folder if necessry..\n\tif not os.path.exists(pathGNRparentFolder):\n\t\tos.makedirs(pathGNRparentFolder)\n\n\t# Remove already present copy of GNR library, if any..\n\tif os.path.exists(pathGNRfolder):\n\t\tshutil.rmtree(pathGNRfolder)\n\n\tresourceGNRfolder = \"smaliRes\" + os.sep + \"grabnrun\"\n\n\t# Copy from the smaliRes folder GNR smali classes\n\tshutil.copytree(resourceGNRfolder, pathGNRfolder)\n\n\tprint \"[In progress] Creating RepackHandler.smali class\"\n\n\trepackHandlerFilePath = pathGNRfolder + os.sep + \"RepackHandler.smali\"\n\trepackHandlerTailPath = pathGNRfolder + os.sep + \"RepackHandlerTail.smali\"\n\n\twith open(repackHandlerFilePath, 'a') as repackHandlerFile:\n\n\t\tif hasStaticAssociativeMap:\n\t\t\t# Simply consider all the entries as tuples of package names\n\t\t\t# and certificate remote URL. Populate the final map accordingly..\n\t\t\tfor packageName in entriesDictionary.keys():\n\n\t\t\t\t# Get the certificate remote URL.\n\t\t\t\tremoteCertificateURL = entriesDictionary[packageName]\n\n\t\t\t\tif isAValidPackageName(packageName):\n\t\t\t\t\tif isARemoteURL(remoteCertificateURL, True):\n\t\t\t\t\t\tlinkPackageNameToCertURL(repackHandlerFile, packageName, entriesDictionary[packageName])\n\t\t\t\t\telse:\n\t\t\t\t\t\tprint \"[Warning] Found an invalid remote certificate URL \" + remoteCertificateURL + \". The linked package name will be skipped!\"\n\t\t\t\telse:\n\t\t\t\t\tprint \"[Warning] Found an invalid package name \" + packageName + \". It will be skipped!\"\n\n\t\t\t# Finally set the hasStaticAssociativeMap attribute to True\n\t\t\tsetHasStaticAssociativeMapBool(repackHandlerFile, True)\n\n\t\telse:\n\n\t\t\t# Flag initialization variable for package names set\n\t\t\tneedToReinitializePackageNameSet = False\n\n\t\t\t# Each entry contains a container and a remote certificate URL that should be used\n\t\t\t# to validate all classes in its package names\n\t\t\tfor containerPath in entriesDictionary.keys():\n\n\t\t\t\t# Get the certificate remote URL.\n\t\t\t\tremoteCertificateURL = entriesDictionary[containerPath]\n\n\t\t\t\t# Check that the remote URL of the certificate is valid..\n\t\t\t\tif isARemoteURL(remoteCertificateURL, True):\n\t\t\t\t\t\n\t\t\t\t\t# Check whether this entry is the special one used for default case\n\t\t\t\t\tif containerPath == 'default':\n\t\t\t\t\t\tlinkPackageNameToCertURL(repackHandlerFile, containerPath, remoteCertificateURL)\n\n\t\t\t\t\telse:\n\t\t\t\t\t\t# Check whether this container path is a remote URL..\n\t\t\t\t\t\tif isARemoteURL(containerPath):\n\n\t\t\t\t\t\t\t# If remote, download the remote container at first..\n\t\t\t\t\t\t\tlocalContPath = downloadRemoteContainer(containerPath)\n\n\t\t\t\t\t\t\tif localContPath:\n\n\t\t\t\t\t\t\t\t# Make a write iteration to RepackHandler smali file\n\t\t\t\t\t\t\t\tmakeOneWriteIteration(repackHandlerFile, needToReinitializePackageNameSet, localContPath, remoteCertificateURL, containerPath)\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t# Reinitialize always after the first population of the set.\n\t\t\t\t\t\t\t\tif not needToReinitializePackageNameSet:\n\t\t\t\t\t\t\t\t\tneedToReinitializePackageNameSet = True\n\n\t\t\t\t\t\t\t\t# Remove downloaded container at local path\n\t\t\t\t\t\t\t\tos.remove(localContPath)\n\n\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t# Check that the local file is a valid container\n\t\t\t\t\t\t\tif isAValidContainer(containerPath):\n\n\t\t\t\t\t\t\t\t# Make a write iteration to RepackHandler smali file\n\t\t\t\t\t\t\t\tmakeOneWriteIteration(repackHandlerFile, needToReinitializePackageNameSet, containerPath, remoteCertificateURL)\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t# Reinitialize always after the first population of the set.\n\t\t\t\t\t\t\t\tif not needToReinitializePackageNameSet:\n\t\t\t\t\t\t\t\t\tneedToReinitializePackageNameSet = True\n\t\t\t\t\t\t\n\t\t\t\t\t\t\telse:\n\t\t\t\t\t\t\t\tprint \"[Warning] Found an invalid container \" + containerPath + \". This resource will be skipped!\"\n\n\t\t\t\telse:\n\t\t\t\t\tprint \"[Warning] Found an invalid remote certificate URL \" + remoteCertificateURL + \". The linked resource will be skipped!\"\n\n\t\t\t# Finally set the hasStaticAssociativeMap attribute to False\n\t\t\tsetHasStaticAssociativeMapBool(repackHandlerFile, False)\n\n\t\t# In the end append the content of RepackHandlerTail.smali to RepackHandler.smali\n\t\twith open(repackHandlerTailPath, 'r') as repackHandlerTail:\n\n\t\t\t# Copy the whole content..\n\t\t\tlinesToAppend = repackHandlerTail.read()\n\t\t\t# ..and append it.\n\t\t\trepackHandlerFile.write(linesToAppend)\n\n\t\t# Finally remove the helper tail file\n\t\tos.remove(repackHandlerTailPath)\n\n\tprint \"[In progress] RepackHandler.smali class successfully created.\"\n\ndef makeOneWriteIteration(repackHandlerFile, needToReinitializePackageNameSet, containerPath, remoteCertificateURL, remoteContReference = ''):\n\n\t# A valid container was found. At first extract the package names\n\t# list from this container\n\tpackageNamesList = extractPackageNamesFromLocalContainer(containerPath)\n\n\tif needToReinitializePackageNameSet:\n\t\treinitializeSet(repackHandlerFile)\n\n\t# Insert all package names in a set in the smali class. \n\tfor packageName in packageNamesList:\n\t\tinsertPackageNameInSet(repackHandlerFile, packageName)\n\n\t# Link the extracted package names to its remote container, if provided..\n\tif remoteContReference:  \n\t\tlinkContainerToCurrentPackageNameSet(repackHandlerFile, remoteContReference)\n\n\t# Compute digest on the downloaded local copy of the file.\n\tcontainerDigest = computeDigestEncode(containerPath)\n\n\tif containerDigest:\n\t\t# Link the extracted package names to the digest as well.\n\t\tlinkContainerToCurrentPackageNameSet(repackHandlerFile, containerDigest)\n\n\t# Finally associate each package name to the remote URL of the certificate\n\tfor packageName in packageNamesList:\n\t\tlinkPackageNameToCertURL(repackHandlerFile, packageName, remoteCertificateURL)\n\ndef reinitializeSet(repackHandlerFile):\n\n\trepackHandlerFile.write('    new-instance v1, Ljava/util/HashSet;' + 2 * '\\n')\n\trepackHandlerFile.write('    .end local v1    # \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"' + '\\n')\n\trepackHandlerFile.write('    invoke-direct {v1}, Ljava/util/HashSet;-><init>()V' + 2 * '\\n')\n\trepackHandlerFile.write('    .restart local v1    # \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"' + 2 * '\\n')\n\n\tprint '[DEBUG] Set of package names reinitialized.'\n\ndef insertPackageNameInSet(repackHandlerFile, packageName):\n\n\trepackHandlerFile.write('    const-string v2, \"' + packageName + '\"' + 2 * '\\n')\n\trepackHandlerFile.write('    invoke-interface {v1, v2}, Ljava/util/Set;->add(Ljava/lang/Object;)Z' + 2 * '\\n')\n\n\tprint '[DEBUG] Added package name ' + packageName + ' to set.'\n\ndef linkContainerToCurrentPackageNameSet(repackHandlerFile, container):\n\n\trepackHandlerFile.write('    sget-object v2, Lit/necst/grabnrun/RepackHandler;->containerToPackageNamesMap:Ljava/util/Map;' + 2 * '\\n')\n\trepackHandlerFile.write('    const-string v3, \"' + container + '\"' + 2 * '\\n')\n\trepackHandlerFile.write('    invoke-interface {v2, v3, v1}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;' + 2 * '\\n')\n\n\tprint '[DEBUG] Linked container ' + container + ' to current set of package names.'\t\n\ndef linkPackageNameToCertURL(repackHandlerFile, packageName, certificateURL):\n\n\trepackHandlerFile.write('    sget-object v2, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;' + 2 * '\\n')\n\trepackHandlerFile.write('    const-string v3, \"' + packageName + '\"' + 2 * '\\n')\n\trepackHandlerFile.write('    const-string v4, \"' + certificateURL + '\"' + 2 * '\\n')\n\trepackHandlerFile.write('    invoke-interface {v2, v3, v4}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;' + 2 * '\\n')\n\n\tprint '[DEBUG] Linked package name ' + packageName + ' to remote certificate at ' + certificateURL\n\ndef setHasStaticAssociativeMapBool(repackHandlerFile, boolValue):\n\n\tif boolValue:\n\t\trepackHandlerFile.write('    const/4 v2, 0x1' + 2 * '\\n')\n\telse:\n\t\trepackHandlerFile.write('    const/4 v2, 0x0' + 2 * '\\n')\n\n\tprint '[DEBUG] Set attribute hasStaticAssociativeMap to ' + str(boolValue)\n\ndef main(argv):\n\n\t# An argument parser is set up to handle user command line input.\n\tparser = argparse.ArgumentParser(description='Process an APK to automatically port it to use GNR secure API for dynamic code loading.')\n\tparser.add_argument('-p', '--preference-file', metavar = 'local file path', help='local path pointing to the preference file used to choose how the repackaging operation will be handled. If missing, a GUI will be shown to configure the required parameters')\n\tparser.add_argument('-k', '--keep-resources', action='store_true', help='avoid temporary files being erased at the end of the process')\n\n\targs = parser.parse_args()\n\n\t# Default path for the preference file in case that the GUI is used to\n\t# select preferences.\n\tuserPrefsFilePath = os.curdir + os.sep + \"preferences\"\n\n\tif args.preference_file:\n\n\t\t# The user provide the location of a preference file so check that this\n\t\t# file actually exists..\n\t\tuserPrefsFilePath = args.preference_file\n\n\t\tif os.path.exists(userPrefsFilePath) and os.path.isfile(userPrefsFilePath):\n\t\t\t# The provided preference path seems acceptable. Start the program\n\t\t\tprint \"[Start] User provided a preference file located at \" + userPrefsFilePath\n\t\telse:\n\t\t\t# The provided preference path is invalid\n\t\t\tprint \"[Error] No preference file was found at the provided location! Aborting..\"\n\t\t\tsys.exit(FAILURE)\n\n\telse:\n\n\t\t# Invoke the Java GUI to recover user preferences on the \n\t\t# repackaging operation\n\t\tprint \"[Start] A GUI is shown to select repackaging options.\"\n\t\tinputSelector = subprocess.call([\"java\", \"-jar\", \"libs\" + os.sep + \"RepackInputSelector.jar\"])\n\n\n\t\tif inputSelector != SUCCESS:\n\t\t\t# User aborted the process. No repackaging will be done\n\t\t\tprint \"[Exit] User aborted the parameter selection steps. No repackaging..\"\n\t\t\tsys.exit(FAILURE)\n\n\t# User completes successfully the preferences \n\t# selection step. Now the repackaging operation starts..\n\trebuiltAPK = \"\"\n\n\twith open(userPrefsFilePath, 'r') as userPrefsFile:\n\t\t\n\t\t# First line must be the apk path.\n\t\tapkPath = userPrefsFile.readline().rstrip();\n\n\t\t# Check that this path is actually pointing to\n\t\t# a valid APK container. \n\t\tif isAValidContainer(apkPath, True):\n\n\t\t\t# Start Androguard analysis on this APK\n\t\t\t# Here we also check whether this APK needs to be patched\n\t\t\tmissingPerms, classesWithDynCodeLoad = performAnalysis(apkPath)\n\n\t\t\t# print missingPerms\n\t\t\t# print classesWithDynCodeLoad\n\n\t\t\t# At first this APK should be decoded with apktool.\n\t\t\tdecodeDirName = decodeTargetAPK(apkPath)\n\n\t\t\t# Then missing permissions, if any, must be added.\n\t\t\taddMissingPermsToAndroidManifest(decodeDirName, missingPerms)\n\n\t\t\t# Next all extension classes of Activities and classes\n\t\t\t# which uses dynamic code loading must be patched.\n\t\t\tpatchSmaliClasses(decodeDirName, classesWithDynCodeLoad)\n\n\t\t\t# In the end the RepackHandler should be set up\n\t\t\t# depending on user preferences. Here smali classes from GNR\n\t\t\t# library will be copied as well.\n\n\t\t\t## Retrieve user preferences (first the boolean value)\n\t\t\thasStaticAssociativeMap = userPrefsFile.readline().rstrip().lower().capitalize();\n\t\t\t\n\t\t\t## Required casting from String to bool value\n\t\t\tif hasStaticAssociativeMap == 'True':\n\t\t\t\thasStaticAssociativeMap = True\n\t\t\telse:\n\t\t\t\tif hasStaticAssociativeMap == 'False':\n\t\t\t\t\thasStaticAssociativeMap = False\n\t\t\t\telse:\n\t\t\t\t\tprint \"[Error] Invalid format of the preference file! Second line should be a True/False choice. Aborting..\"\n\t\t\t\t\tif not args.keep_resources:\n\t\t\t\t\t\tshutil.rmtree(decodeDirName)\n\t\t\t\t\tsys.exit(FAILURE)\n\n\t\t\t## Initialize dictionary and add entries in the file to it\n\t\t\tentriesDictionary = {}\n\n\t\t\tfor line in userPrefsFile:\n\n\t\t\t\t# Split the line according to the separator..\n\t\t\t\tsubfields = line.split(\"|\")\n\n\t\t\t\t# Sanity check on the preference file format\n\t\t\t\tif len(subfields) != 2:\n\t\t\t\t\tprint \"[Error] Invalid format of the preference file! Aborting..\"\n\t\t\t\t\tif not args.keep_resources:\n\t\t\t\t\t\tshutil.rmtree(decodeDirName)\n\t\t\t\t\tsys.exit(FAILURE)\n\n\t\t\t\tentriesDictionary[subfields[0].strip()] = subfields[1].strip()\n\n\t\t\t#print entriesDictionary\n\n\t\t\tsetUpRepackHandler(decodeDirName, hasStaticAssociativeMap, entriesDictionary)\n\n\t\t\t# Finally rebuild the APK with the patched resources\n\t\t\trebuiltAPK = buildRepackagedAPK(decodeDirName)\n\n\t\t\t# Copy repackaged APK in main folder\n\t\t\tshutil.copy(rebuiltAPK, os.getcwd())\n\n\t\t\t# Raise success flag\n\t\t\trepackageSuccessful = True\n\n\t\telse:\n\n\t\t\t# The preference file did not point to a valid APK container\n\t\t\tprint \"[Error] Invalid format of the preference file! First line should point to a valid APK. Aborting..\"\n\t\t\tsys.exit(FAILURE)\n\n\t# Clean up of all the resources\n\t# os.remove(userPrefsFilePath)\n\tif not args.keep_resources:\n\t\tshutil.rmtree(decodeDirName)\n\n\t# The repackaging process is finished with no errors :)\n\tfinalPath = os.getcwd() + os.sep + os.path.basename(rebuiltAPK)\n\tprint \"[Success] Target APK was successfully patched! Result container can be found at \" + finalPath\n\tsys.exit(SUCCESS)\n\nif __name__ == \"__main__\":\n\tmain(sys.argv[1:])"
  },
  {
    "path": "repackPOC/requirements.txt",
    "content": "# Required modules you will need on your machine in order to run\n# 'Grab'n Run repackaging tool\".\n# Type in a terminal the following command:\n# pip install -r /<local-path-to-GNR-folder>/repack/requirements.txt\n\n# You will also need to setup a local copy of Androguard to run this tool\n# and to link its local folder to the variable \"androguard_location\" \n# at the beginning of the file named \"repackagingTool.py\".\n\n# For further details on Androguard installation see:\n# https://code.google.com/p/androguard/wiki/Installation\n\n# Finally notice that this script requires a Python version >= 2.6 and <= 3.\n# To run the script simply type:\n# python repackagingTool.py\n\nargparse>=1.2.1\npyparsing>=2.0.1\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/CacheLogger.smali",
    "content": ".class final Lit/necst/grabnrun/CacheLogger;\n.super Ljava/lang/Object;\n.source \"CacheLogger.java\"\n\n\n# static fields\n.field private static final CREATION_TIMESTAMP:I = 0x2\n\n.field private static final ELEMENTS_PER_LOG_LINE:I = 0x3\n\n.field private static final LOCAL_FILE_NAME:I = 0x1\n\n.field private static final REMOTE_URL:I = 0x0\n\n.field private static final TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n.field private static final helperFileName:Ljava/lang/String; = \"helper.txt\"\n\n\n# instance fields\n.field private cacheDirectoryPath:Ljava/lang/String;\n\n.field private hasBeenAlreadyFinalized:Z\n\n.field private helperFile:Ljava/io/File;\n\n.field private remoteURLToCreationTimestamp:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Long;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field private remoteURLToLocalFileMap:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 1\n\n    .prologue\n    .line 44\n    const-class v0, Lit/necst/grabnrun/CacheLogger;\n\n    invoke-virtual {v0}, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;\n\n    move-result-object v0\n\n    sput-object v0, Lit/necst/grabnrun/CacheLogger;->TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n    .line 62\n    return-void\n.end method\n\n.method constructor <init>(Ljava/lang/String;I)V\n    .locals 14\n    .param p1, \"cacheDirectoryPath\"    # Ljava/lang/String;\n    .param p2, \"daysTillConsideredFresh\"    # I\n\n    .prologue\n    .line 81\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 85\n    new-instance v7, Ljava/util/HashMap;\n\n    invoke-direct {v7}, Ljava/util/HashMap;-><init>()V\n\n    iput-object v7, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    .line 86\n    new-instance v7, Ljava/util/HashMap;\n\n    invoke-direct {v7}, Ljava/util/HashMap;-><init>()V\n\n    iput-object v7, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToCreationTimestamp:Ljava/util/Map;\n\n    .line 88\n    iput-object p1, p0, Lit/necst/grabnrun/CacheLogger;->cacheDirectoryPath:Ljava/lang/String;\n\n    .line 90\n    const/4 v7, 0x0\n\n    iput-boolean v7, p0, Lit/necst/grabnrun/CacheLogger;->hasBeenAlreadyFinalized:Z\n\n    .line 95\n    new-instance v7, Ljava/io/File;\n\n    new-instance v10, Ljava/lang/StringBuilder;\n\n    invoke-static {p1}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-direct {v10, v11}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v11, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v10\n\n    const-string v11, \"helper.txt\"\n\n    invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v10\n\n    invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v10\n\n    invoke-direct {v7, v10}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    iput-object v7, p0, Lit/necst/grabnrun/CacheLogger;->helperFile:Ljava/io/File;\n\n    .line 97\n    iget-object v7, p0, Lit/necst/grabnrun/CacheLogger;->helperFile:Ljava/io/File;\n\n    invoke-virtual {v7}, Ljava/io/File;->exists()Z\n\n    move-result v7\n\n    if-eqz v7, :cond_1\n\n    .line 99\n    const/4 v5, 0x0\n\n    .line 105\n    .local v5, \"in\":Ljava/util/Scanner;\n    :try_start_0\n    new-instance v7, Ljava/util/Scanner;\n\n    iget-object v10, p0, Lit/necst/grabnrun/CacheLogger;->helperFile:Ljava/io/File;\n\n    invoke-direct {v7, v10}, Ljava/util/Scanner;-><init>(Ljava/io/File;)V\n\n    const-string v10, \";\\n\"\n\n    invoke-virtual {v7, v10}, Ljava/util/Scanner;->useDelimiter(Ljava/lang/String;)Ljava/util/Scanner;\n\n    move-result-object v5\n\n    .line 107\n    :cond_0\n    :goto_0\n    invoke-virtual {v5}, Ljava/util/Scanner;->hasNext()Z\n    :try_end_0\n    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    move-result v7\n\n    if-nez v7, :cond_2\n\n    .line 146\n    if-eqz v5, :cond_1\n\n    .line 147\n    invoke-virtual {v5}, Ljava/util/Scanner;->close()V\n\n    .line 151\n    .end local v5    # \"in\":Ljava/util/Scanner;\n    :cond_1\n    :goto_1\n    return-void\n\n    .line 110\n    .restart local v5    # \"in\":Ljava/util/Scanner;\n    :cond_2\n    :try_start_1\n    invoke-virtual {v5}, Ljava/util/Scanner;->next()Ljava/lang/String;\n\n    move-result-object v1\n\n    .line 111\n    .local v1, \"currentLine\":Ljava/lang/String;\n    const-string v7, \" \"\n\n    invoke-virtual {v1, v7}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;\n\n    move-result-object v6\n\n    .line 113\n    .local v6, \"lineTokens\":[Ljava/lang/String;\n    array-length v7, v6\n\n    const/4 v10, 0x3\n\n    if-ne v7, v10, :cond_0\n\n    .line 115\n    new-instance v0, Ljava/io/File;\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    invoke-static {p1}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v10\n\n    invoke-direct {v7, v10}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v10, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual {v7, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    const/4 v10, 0x1\n\n    aget-object v10, v6, v10\n\n    invoke-virtual {v7, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v0, v7}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 117\n    .local v0, \"checkContainerFile\":Ljava/io/File;\n    invoke-virtual {v0}, Ljava/io/File;->exists()Z\n\n    move-result v7\n\n    if-eqz v7, :cond_0\n\n    .line 121\n    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J\n\n    move-result-wide v10\n\n    const/4 v7, 0x2\n\n    aget-object v7, v6, v7\n\n    invoke-static {v7}, Ljava/lang/Long;->valueOf(Ljava/lang/String;)Ljava/lang/Long;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/Long;->longValue()J\n\n    move-result-wide v12\n\n    sub-long v2, v10, v12\n\n    .line 124\n    .local v2, \"currentLivedTime\":J\n    mul-int/lit8 v7, p2, 0x18\n\n    mul-int/lit8 v7, v7, 0x3c\n\n    mul-int/lit16 v7, v7, 0x3e8\n\n    int-to-long v8, v7\n\n    .line 126\n    .local v8, \"maximumTimeToLive\":J\n    cmp-long v7, v2, v8\n\n    if-gez v7, :cond_3\n\n    .line 129\n    iget-object v7, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    const/4 v10, 0x0\n\n    aget-object v10, v6, v10\n\n    const/4 v11, 0x1\n\n    aget-object v11, v6, v11\n\n    invoke-interface {v7, v10, v11}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 130\n    iget-object v7, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToCreationTimestamp:Ljava/util/Map;\n\n    const/4 v10, 0x0\n\n    aget-object v10, v6, v10\n\n    const/4 v11, 0x2\n\n    aget-object v11, v6, v11\n\n    invoke-static {v11}, Ljava/lang/Long;->valueOf(Ljava/lang/String;)Ljava/lang/Long;\n\n    move-result-object v11\n\n    invoke-interface {v7, v10, v11}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n    :try_end_1\n    .catch Ljava/io/FileNotFoundException; {:try_start_1 .. :try_end_1} :catch_0\n    .catchall {:try_start_1 .. :try_end_1} :catchall_0\n\n    goto :goto_0\n\n    .line 142\n    .end local v0    # \"checkContainerFile\":Ljava/io/File;\n    .end local v1    # \"currentLine\":Ljava/lang/String;\n    .end local v2    # \"currentLivedTime\":J\n    .end local v6    # \"lineTokens\":[Ljava/lang/String;\n    .end local v8    # \"maximumTimeToLive\":J\n    :catch_0\n    move-exception v4\n\n    .line 143\n    .local v4, \"e\":Ljava/io/FileNotFoundException;\n    :try_start_2\n    sget-object v7, Lit/necst/grabnrun/CacheLogger;->TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n    const-string v10, \"Issue while opening helper file!\"\n\n    invoke-static {v7, v10}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_2\n    .catchall {:try_start_2 .. :try_end_2} :catchall_0\n\n    .line 146\n    if-eqz v5, :cond_1\n\n    .line 147\n    invoke-virtual {v5}, Ljava/util/Scanner;->close()V\n\n    goto :goto_1\n\n    .line 135\n    .end local v4    # \"e\":Ljava/io/FileNotFoundException;\n    .restart local v0    # \"checkContainerFile\":Ljava/io/File;\n    .restart local v1    # \"currentLine\":Ljava/lang/String;\n    .restart local v2    # \"currentLivedTime\":J\n    .restart local v6    # \"lineTokens\":[Ljava/lang/String;\n    .restart local v8    # \"maximumTimeToLive\":J\n    :cond_3\n    :try_start_3\n    invoke-virtual {v0}, Ljava/io/File;->delete()Z\n\n    move-result v7\n\n    if-eqz v7, :cond_0\n\n    .line 136\n    sget-object v7, Lit/necst/grabnrun/CacheLogger;->TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n    new-instance v10, Ljava/lang/StringBuilder;\n\n    const-string v11, \"Issue while erasing \"\n\n    invoke-direct {v10, v11}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v0}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-virtual {v10, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v10\n\n    invoke-virtual {v10}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v10\n\n    invoke-static {v7, v10}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_3\n    .catch Ljava/io/FileNotFoundException; {:try_start_3 .. :try_end_3} :catch_0\n    .catchall {:try_start_3 .. :try_end_3} :catchall_0\n\n    goto/16 :goto_0\n\n    .line 144\n    .end local v0    # \"checkContainerFile\":Ljava/io/File;\n    .end local v1    # \"currentLine\":Ljava/lang/String;\n    .end local v2    # \"currentLivedTime\":J\n    .end local v6    # \"lineTokens\":[Ljava/lang/String;\n    .end local v8    # \"maximumTimeToLive\":J\n    :catchall_0\n    move-exception v7\n\n    .line 146\n    if-eqz v5, :cond_4\n\n    .line 147\n    invoke-virtual {v5}, Ljava/util/Scanner;->close()V\n\n    .line 149\n    :cond_4\n    throw v7\n.end method\n\n\n# virtual methods\n.method final addCachedEntryToLog(Ljava/lang/String;Ljava/lang/String;)V\n    .locals 4\n    .param p1, \"remoteURL\"    # Ljava/lang/String;\n    .param p2, \"localFileName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 190\n    iget-boolean v0, p0, Lit/necst/grabnrun/CacheLogger;->hasBeenAlreadyFinalized:Z\n\n    if-eqz v0, :cond_0\n\n    .line 195\n    :goto_0\n    return-void\n\n    .line 193\n    :cond_0\n    iget-object v0, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToCreationTimestamp:Ljava/util/Map;\n\n    invoke-static {}, Ljava/lang/System;->currentTimeMillis()J\n\n    move-result-wide v2\n\n    invoke-static {v2, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;\n\n    move-result-object v1\n\n    invoke-interface {v0, p1, v1}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 194\n    iget-object v0, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v0, p1, p2}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    goto :goto_0\n.end method\n\n.method final checkForCachedEntry(Ljava/lang/String;)Ljava/lang/String;\n    .locals 4\n    .param p1, \"remoteURL\"    # Ljava/lang/String;\n\n    .prologue\n    const/4 v1, 0x0\n\n    .line 165\n    iget-boolean v0, p0, Lit/necst/grabnrun/CacheLogger;->hasBeenAlreadyFinalized:Z\n\n    if-eqz v0, :cond_0\n\n    move-object v0, v1\n\n    .line 174\n    :goto_0\n    return-object v0\n\n    .line 169\n    :cond_0\n    iget-object v0, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v0, p1}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_1\n\n    .line 170\n    new-instance v2, Ljava/io/File;\n\n    new-instance v0, Ljava/lang/StringBuilder;\n\n    iget-object v3, p0, Lit/necst/grabnrun/CacheLogger;->cacheDirectoryPath:Ljava/lang/String;\n\n    invoke-static {v3}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-direct {v0, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v3, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v3\n\n    iget-object v0, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v0, p1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Ljava/lang/String;\n\n    invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v0\n\n    invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v0\n\n    invoke-direct {v2, v0}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v2}, Ljava/io/File;->exists()Z\n\n    move-result v0\n\n    if-eqz v0, :cond_1\n\n    .line 171\n    iget-object v0, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v0, p1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Ljava/lang/String;\n\n    goto :goto_0\n\n    :cond_1\n    move-object v0, v1\n\n    .line 174\n    goto :goto_0\n.end method\n\n.method final finalizeLog()V\n    .locals 7\n\n    .prologue\n    .line 209\n    iget-boolean v5, p0, Lit/necst/grabnrun/CacheLogger;->hasBeenAlreadyFinalized:Z\n\n    if-eqz v5, :cond_1\n\n    .line 255\n    :cond_0\n    :goto_0\n    return-void\n\n    .line 210\n    :cond_1\n    const/4 v5, 0x1\n\n    iput-boolean v5, p0, Lit/necst/grabnrun/CacheLogger;->hasBeenAlreadyFinalized:Z\n\n    .line 213\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->helperFile:Ljava/io/File;\n\n    invoke-virtual {v5}, Ljava/io/File;->exists()Z\n\n    move-result v5\n\n    if-eqz v5, :cond_2\n\n    .line 214\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->helperFile:Ljava/io/File;\n\n    invoke-virtual {v5}, Ljava/io/File;->delete()Z\n\n    move-result v5\n\n    if-nez v5, :cond_2\n\n    .line 215\n    sget-object v5, Lit/necst/grabnrun/CacheLogger;->TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n    const-string v6, \"Problem while erasing old copy of helper file!\"\n\n    invoke-static {v5, v6}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 217\n    :cond_2\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v5}, Ljava/util/Map;->isEmpty()Z\n\n    move-result v5\n\n    if-nez v5, :cond_0\n\n    .line 222\n    const/4 v2, 0x0\n\n    .line 226\n    .local v2, \"mPrintWriter\":Ljava/io/PrintWriter;\n    :try_start_0\n    new-instance v3, Ljava/io/PrintWriter;\n\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->helperFile:Ljava/io/File;\n\n    invoke-direct {v3, v5}, Ljava/io/PrintWriter;-><init>(Ljava/io/File;)V\n    :try_end_0\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_1\n    .catchall {:try_start_0 .. :try_end_0} :catchall_1\n\n    .line 228\n    .end local v2    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .local v3, \"mPrintWriter\":Ljava/io/PrintWriter;\n    :try_start_1\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v5}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v5\n\n    invoke-interface {v5}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v4\n\n    .line 230\n    .local v4, \"remoteURLIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_3\n    :goto_1\n    invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v5\n\n    if-nez v5, :cond_4\n\n    .line 242\n    invoke-virtual {v3}, Ljava/io/PrintWriter;->checkError()Z\n\n    move-result v5\n\n    if-eqz v5, :cond_6\n\n    .line 243\n    new-instance v5, Ljava/io/IOException;\n\n    invoke-direct {v5}, Ljava/io/IOException;-><init>()V\n\n    throw v5\n    :try_end_1\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_0\n    .catchall {:try_start_1 .. :try_end_1} :catchall_0\n\n    .line 247\n    .end local v4    # \"remoteURLIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :catch_0\n    move-exception v1\n\n    move-object v2, v3\n\n    .line 248\n    .end local v3    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .local v1, \"e\":Ljava/io/IOException;\n    .restart local v2    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    :goto_2\n    :try_start_2\n    sget-object v5, Lit/necst/grabnrun/CacheLogger;->TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n    const-string v6, \"Problem while updating helper file!\"\n\n    invoke-static {v5, v6}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_2\n    .catchall {:try_start_2 .. :try_end_2} :catchall_1\n\n    .line 251\n    if-eqz v2, :cond_0\n\n    .line 252\n    invoke-virtual {v2}, Ljava/io/PrintWriter;->close()V\n\n    goto :goto_0\n\n    .line 232\n    .end local v1    # \"e\":Ljava/io/IOException;\n    .end local v2    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .restart local v3    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .restart local v4    # \"remoteURLIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_4\n    :try_start_3\n    invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Ljava/lang/String;\n\n    .line 234\n    .local v0, \"currentRemoteURL\":Ljava/lang/String;\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToCreationTimestamp:Ljava/util/Map;\n\n    invoke-interface {v5, v0}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v5\n\n    if-eqz v5, :cond_3\n\n    .line 238\n    new-instance v5, Ljava/lang/StringBuilder;\n\n    invoke-static {v0}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v6\n\n    invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v6, \" \"\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    iget-object v5, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToLocalFileMap:Ljava/util/Map;\n\n    invoke-interface {v5, v0}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v5\n\n    check-cast v5, Ljava/lang/String;\n\n    invoke-virtual {v6, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    const-string v6, \" \"\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    iget-object v6, p0, Lit/necst/grabnrun/CacheLogger;->remoteURLToCreationTimestamp:Ljava/util/Map;\n\n    invoke-interface {v6, v0}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v6\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    const-string v6, \";\"\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v5\n\n    invoke-virtual {v3, v5}, Ljava/io/PrintWriter;->println(Ljava/lang/String;)V\n    :try_end_3\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_0\n    .catchall {:try_start_3 .. :try_end_3} :catchall_0\n\n    goto :goto_1\n\n    .line 249\n    .end local v0    # \"currentRemoteURL\":Ljava/lang/String;\n    .end local v4    # \"remoteURLIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :catchall_0\n    move-exception v5\n\n    move-object v2, v3\n\n    .line 251\n    .end local v3    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .restart local v2    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    :goto_3\n    if-eqz v2, :cond_5\n\n    .line 252\n    invoke-virtual {v2}, Ljava/io/PrintWriter;->close()V\n\n    .line 253\n    :cond_5\n    throw v5\n\n    .line 245\n    .end local v2    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .restart local v3    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .restart local v4    # \"remoteURLIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_6\n    :try_start_4\n    sget-object v5, Lit/necst/grabnrun/CacheLogger;->TAG_FILE_CACHE_LOGGER:Ljava/lang/String;\n\n    const-string v6, \"Helper file was correctly stored on the device.\"\n\n    invoke-static {v5, v6}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_4\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_0\n    .catchall {:try_start_4 .. :try_end_4} :catchall_0\n\n    .line 251\n    if-eqz v3, :cond_0\n\n    .line 252\n    invoke-virtual {v3}, Ljava/io/PrintWriter;->close()V\n\n    goto/16 :goto_0\n\n    .line 249\n    .end local v3    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    .end local v4    # \"remoteURLIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .restart local v2    # \"mPrintWriter\":Ljava/io/PrintWriter;\n    :catchall_1\n    move-exception v5\n\n    goto :goto_3\n\n    .line 247\n    :catch_1\n    move-exception v1\n\n    goto :goto_2\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/CertFileFilter.smali",
    "content": ".class final Lit/necst/grabnrun/CertFileFilter;\n.super Ljava/lang/Object;\n.source \"CertFileFilter.java\"\n\n# interfaces\n.implements Ljava/io/FileFilter;\n\n\n# instance fields\n.field private certificateName:Ljava/lang/String;\n\n.field private final okCertsExtensions:[Ljava/lang/String;\n\n\n# direct methods\n.method constructor <init>(Ljava/lang/String;)V\n    .locals 3\n    .param p1, \"certificateName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 46\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 32\n    const/4 v0, 0x1\n\n    new-array v0, v0, [Ljava/lang/String;\n\n    const/4 v1, 0x0\n\n    const-string v2, \".pem\"\n\n    aput-object v2, v0, v1\n\n    iput-object v0, p0, Lit/necst/grabnrun/CertFileFilter;->okCertsExtensions:[Ljava/lang/String;\n\n    .line 48\n    iput-object p1, p0, Lit/necst/grabnrun/CertFileFilter;->certificateName:Ljava/lang/String;\n\n    .line 49\n    return-void\n.end method\n\n\n# virtual methods\n.method public final accept(Ljava/io/File;)Z\n    .locals 8\n    .param p1, \"file\"    # Ljava/io/File;\n\n    .prologue\n    const/4 v1, 0x0\n\n    .line 56\n    invoke-virtual {p1}, Ljava/io/File;->isDirectory()Z\n\n    move-result v2\n\n    if-eqz v2, :cond_1\n\n    .line 71\n    :cond_0\n    :goto_0\n    return v1\n\n    .line 58\n    :cond_1\n    invoke-virtual {p1}, Ljava/io/File;->isFile()Z\n\n    move-result v2\n\n    if-eqz v2, :cond_0\n\n    .line 63\n    iget-object v3, p0, Lit/necst/grabnrun/CertFileFilter;->okCertsExtensions:[Ljava/lang/String;\n\n    array-length v4, v3\n\n    move v2, v1\n\n    :goto_1\n    if-ge v2, v4, :cond_0\n\n    aget-object v0, v3, v2\n\n    .line 65\n    .local v0, \"extension\":Ljava/lang/String;\n    invoke-virtual {p1}, Ljava/io/File;->getName()Ljava/lang/String;\n\n    move-result-object v5\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    iget-object v7, p0, Lit/necst/grabnrun/CertFileFilter;->certificateName:Ljava/lang/String;\n\n    invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    invoke-virtual {v5, v6}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v5\n\n    if-eqz v5, :cond_2\n\n    .line 66\n    const/4 v1, 0x1\n\n    goto :goto_0\n\n    .line 63\n    :cond_2\n    add-int/lit8 v2, v2, 0x1\n\n    goto :goto_1\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/FileDownloader$1.smali",
    "content": ".class Lit/necst/grabnrun/FileDownloader$1;\n.super Ljava/lang/Thread;\n.source \"FileDownloader.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingMethod;\n    value = Lit/necst/grabnrun/FileDownloader;->downloadRemoteUrl(Ljava/net/URL;Ljava/lang/String;Z)Z\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x0\n    name = null\n.end annotation\n\n\n# instance fields\n.field final synthetic this$0:Lit/necst/grabnrun/FileDownloader;\n\n.field private final synthetic val$isRedirectAllowed:Z\n\n.field private final synthetic val$localURI:Ljava/lang/String;\n\n.field private final synthetic val$remoteURL:Ljava/net/URL;\n\n\n# direct methods\n.method constructor <init>(Lit/necst/grabnrun/FileDownloader;Ljava/net/URL;ZLjava/lang/String;)V\n    .locals 0\n\n    .prologue\n    .line 1\n    iput-object p1, p0, Lit/necst/grabnrun/FileDownloader$1;->this$0:Lit/necst/grabnrun/FileDownloader;\n\n    iput-object p2, p0, Lit/necst/grabnrun/FileDownloader$1;->val$remoteURL:Ljava/net/URL;\n\n    iput-boolean p3, p0, Lit/necst/grabnrun/FileDownloader$1;->val$isRedirectAllowed:Z\n\n    iput-object p4, p0, Lit/necst/grabnrun/FileDownloader$1;->val$localURI:Ljava/lang/String;\n\n    .line 93\n    invoke-direct {p0}, Ljava/lang/Thread;-><init>()V\n\n    return-void\n.end method\n\n\n# virtual methods\n.method public run()V\n    .locals 16\n\n    .prologue\n    .line 98\n    const/4 v12, 0x0\n\n    .line 99\n    .local v12, \"urlConnection\":Ljava/net/HttpURLConnection;\n    const/4 v5, 0x0\n\n    .line 100\n    .local v5, \"inputStream\":Ljava/io/InputStream;\n    const/4 v7, 0x0\n\n    .line 104\n    .local v7, \"outputStream\":Ljava/io/OutputStream;\n    :try_start_0\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/FileDownloader$1;->val$remoteURL:Ljava/net/URL;\n\n    invoke-virtual {v13}, Ljava/net/URL;->getProtocol()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"https\"\n\n    invoke-virtual {v13, v14}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_6\n\n    .line 106\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/FileDownloader$1;->val$remoteURL:Ljava/net/URL;\n\n    invoke-virtual {v13}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;\n\n    move-result-object v13\n\n    move-object v0, v13\n\n    check-cast v0, Ljavax/net/ssl/HttpsURLConnection;\n\n    move-object v12, v0\n\n    .line 113\n    :goto_0\n    const/16 v13, 0x3e8\n\n    invoke-virtual {v12, v13}, Ljava/net/HttpURLConnection;->setConnectTimeout(I)V\n\n    .line 117\n    # getter for: Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n    invoke-static {}, Lit/necst/grabnrun/FileDownloader;->access$0()Ljava/lang/String;\n\n    move-result-object v13\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"A connection was set up: \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, p0\n\n    iget-object v15, v0, Lit/necst/grabnrun/FileDownloader$1;->val$remoteURL:Ljava/net/URL;\n\n    invoke-virtual {v15}, Ljava/net/URL;->toString()Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 120\n    move-object/from16 v0, p0\n\n    iget-boolean v13, v0, Lit/necst/grabnrun/FileDownloader$1;->val$isRedirectAllowed:Z\n\n    if-eqz v13, :cond_2\n\n    .line 122\n    const/4 v10, 0x0\n\n    .line 125\n    .local v10, \"redirect\":Z\n    invoke-virtual {v12}, Ljava/net/HttpURLConnection;->getResponseCode()I\n\n    move-result v2\n\n    .line 126\n    .local v2, \"connection_status\":I\n    const/16 v13, 0xc8\n\n    if-eq v2, v13, :cond_1\n\n    .line 127\n    const/16 v13, 0x12e\n\n    if-eq v2, v13, :cond_0\n\n    .line 128\n    const/16 v13, 0x12d\n\n    if-eq v2, v13, :cond_0\n\n    .line 129\n    const/16 v13, 0x12f\n\n    if-ne v2, v13, :cond_1\n\n    .line 131\n    :cond_0\n    const/4 v10, 0x1\n\n    .line 134\n    :cond_1\n    if-eqz v10, :cond_2\n\n    .line 137\n    new-instance v11, Ljava/net/URL;\n\n    const-string v13, \"Location\"\n\n    invoke-virtual {v12, v13}, Ljava/net/HttpURLConnection;->getHeaderField(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v13\n\n    invoke-direct {v11, v13}, Ljava/net/URL;-><init>(Ljava/lang/String;)V\n\n    .line 140\n    .local v11, \"redirectedURL\":Ljava/net/URL;\n    const-string v13, \"Set-Cookie\"\n\n    invoke-virtual {v12, v13}, Ljava/net/HttpURLConnection;->getHeaderField(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v3\n\n    .line 143\n    .local v3, \"cookies\":Ljava/lang/String;\n    invoke-virtual {v11}, Ljava/net/URL;->getProtocol()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"https\"\n\n    invoke-virtual {v13, v14}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_7\n\n    .line 145\n    invoke-virtual {v11}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;\n\n    move-result-object v13\n\n    move-object v0, v13\n\n    check-cast v0, Ljavax/net/ssl/HttpsURLConnection;\n\n    move-object v12, v0\n\n    .line 152\n    :goto_1\n    const/16 v13, 0x3e8\n\n    invoke-virtual {v12, v13}, Ljava/net/HttpURLConnection;->setConnectTimeout(I)V\n\n    .line 154\n    const-string v13, \"Cookie\"\n\n    invoke-virtual {v12, v13, v3}, Ljava/net/HttpURLConnection;->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V\n\n    .line 156\n    # getter for: Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n    invoke-static {}, Lit/necst/grabnrun/FileDownloader;->access$0()Ljava/lang/String;\n\n    move-result-object v13\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"The connection was redirected to: \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v11}, Ljava/net/URL;->toString()Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 161\n    .end local v2    # \"connection_status\":I\n    .end local v3    # \"cookies\":Ljava/lang/String;\n    .end local v10    # \"redirect\":Z\n    .end local v11    # \"redirectedURL\":Ljava/net/URL;\n    :cond_2\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/FileDownloader$1;->this$0:Lit/necst/grabnrun/FileDownloader;\n\n    invoke-virtual {v12}, Ljava/net/HttpURLConnection;->getContentType()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Lit/necst/grabnrun/FileDownloader;->access$1(Lit/necst/grabnrun/FileDownloader;Ljava/lang/String;)V\n\n    .line 165\n    new-instance v6, Ljava/io/BufferedInputStream;\n\n    invoke-virtual {v12}, Ljava/net/HttpURLConnection;->getInputStream()Ljava/io/InputStream;\n\n    move-result-object v13\n\n    invoke-direct {v6, v13}, Ljava/io/BufferedInputStream;-><init>(Ljava/io/InputStream;)V\n    :try_end_0\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_7\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 166\n    .end local v5    # \"inputStream\":Ljava/io/InputStream;\n    .local v6, \"inputStream\":Ljava/io/InputStream;\n    :try_start_1\n    new-instance v8, Ljava/io/FileOutputStream;\n\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/FileDownloader$1;->val$localURI:Ljava/lang/String;\n\n    invoke-direct {v8, v13}, Ljava/io/FileOutputStream;-><init>(Ljava/lang/String;)V\n    :try_end_1\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_8\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    .line 168\n    .end local v7    # \"outputStream\":Ljava/io/OutputStream;\n    .local v8, \"outputStream\":Ljava/io/OutputStream;\n    const/4 v9, 0x0\n\n    .line 169\n    .local v9, \"read\":I\n    const/16 v13, 0x400\n\n    :try_start_2\n    new-array v1, v13, [B\n\n    .line 171\n    .local v1, \"bytes\":[B\n    :goto_2\n    invoke-virtual {v6, v1}, Ljava/io/InputStream;->read([B)I\n\n    move-result v9\n\n    if-gtz v9, :cond_8\n\n    .line 175\n    # getter for: Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n    invoke-static {}, Lit/necst/grabnrun/FileDownloader;->access$0()Ljava/lang/String;\n\n    move-result-object v13\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"Download complete. Container Path: \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, p0\n\n    iget-object v15, v0, Lit/necst/grabnrun/FileDownloader$1;->val$localURI:Ljava/lang/String;\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_0\n    .catchall {:try_start_2 .. :try_end_2} :catchall_2\n\n    .line 183\n    # getter for: Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n    invoke-static {}, Lit/necst/grabnrun/FileDownloader;->access$0()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"Clean up all pending streams..\"\n\n    invoke-static {v13, v14}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 184\n    if-eqz v12, :cond_3\n\n    .line 185\n    invoke-virtual {v12}, Ljava/net/HttpURLConnection;->disconnect()V\n\n    .line 187\n    :cond_3\n    if-eqz v6, :cond_4\n\n    .line 189\n    :try_start_3\n    invoke-virtual {v6}, Ljava/io/InputStream;->close()V\n    :try_end_3\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_5\n\n    .line 194\n    :cond_4\n    :goto_3\n    if-eqz v8, :cond_e\n\n    .line 197\n    :try_start_4\n    invoke-virtual {v8}, Ljava/io/OutputStream;->close()V\n    :try_end_4\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_6\n\n    move-object v7, v8\n\n    .end local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v7    # \"outputStream\":Ljava/io/OutputStream;\n    move-object v5, v6\n\n    .line 204\n    .end local v1    # \"bytes\":[B\n    .end local v6    # \"inputStream\":Ljava/io/InputStream;\n    .end local v9    # \"read\":I\n    .restart local v5    # \"inputStream\":Ljava/io/InputStream;\n    :cond_5\n    :goto_4\n    return-void\n\n    .line 109\n    :cond_6\n    :try_start_5\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/FileDownloader$1;->val$remoteURL:Ljava/net/URL;\n\n    invoke-virtual {v13}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;\n\n    move-result-object v13\n\n    move-object v0, v13\n\n    check-cast v0, Ljava/net/HttpURLConnection;\n\n    move-object v12, v0\n\n    goto/16 :goto_0\n\n    .line 148\n    .restart local v2    # \"connection_status\":I\n    .restart local v3    # \"cookies\":Ljava/lang/String;\n    .restart local v10    # \"redirect\":Z\n    .restart local v11    # \"redirectedURL\":Ljava/net/URL;\n    :cond_7\n    invoke-virtual {v11}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;\n\n    move-result-object v13\n\n    move-object v0, v13\n\n    check-cast v0, Ljava/net/HttpURLConnection;\n\n    move-object v12, v0\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_7\n    .catchall {:try_start_5 .. :try_end_5} :catchall_0\n\n    goto/16 :goto_1\n\n    .line 172\n    .end local v2    # \"connection_status\":I\n    .end local v3    # \"cookies\":Ljava/lang/String;\n    .end local v5    # \"inputStream\":Ljava/io/InputStream;\n    .end local v7    # \"outputStream\":Ljava/io/OutputStream;\n    .end local v10    # \"redirect\":Z\n    .end local v11    # \"redirectedURL\":Ljava/net/URL;\n    .restart local v1    # \"bytes\":[B\n    .restart local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v9    # \"read\":I\n    :cond_8\n    const/4 v13, 0x0\n\n    :try_start_6\n    invoke-virtual {v8, v1, v13, v9}, Ljava/io/OutputStream;->write([BII)V\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_0\n    .catchall {:try_start_6 .. :try_end_6} :catchall_2\n\n    goto :goto_2\n\n    .line 177\n    .end local v1    # \"bytes\":[B\n    :catch_0\n    move-exception v13\n\n    move-object v7, v8\n\n    .end local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v7    # \"outputStream\":Ljava/io/OutputStream;\n    move-object v5, v6\n\n    .line 183\n    .end local v6    # \"inputStream\":Ljava/io/InputStream;\n    .end local v9    # \"read\":I\n    .restart local v5    # \"inputStream\":Ljava/io/InputStream;\n    :goto_5\n    # getter for: Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n    invoke-static {}, Lit/necst/grabnrun/FileDownloader;->access$0()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"Clean up all pending streams..\"\n\n    invoke-static {v13, v14}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 184\n    if-eqz v12, :cond_9\n\n    .line 185\n    invoke-virtual {v12}, Ljava/net/HttpURLConnection;->disconnect()V\n\n    .line 187\n    :cond_9\n    if-eqz v5, :cond_a\n\n    .line 189\n    :try_start_7\n    invoke-virtual {v5}, Ljava/io/InputStream;->close()V\n    :try_end_7\n    .catch Ljava/io/IOException; {:try_start_7 .. :try_end_7} :catch_2\n\n    .line 194\n    :cond_a\n    :goto_6\n    if-eqz v7, :cond_5\n\n    .line 197\n    :try_start_8\n    invoke-virtual {v7}, Ljava/io/OutputStream;->close()V\n    :try_end_8\n    .catch Ljava/io/IOException; {:try_start_8 .. :try_end_8} :catch_1\n\n    goto :goto_4\n\n    .line 198\n    :catch_1\n    move-exception v4\n\n    .line 199\n    .local v4, \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_4\n\n    .line 190\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catch_2\n    move-exception v4\n\n    .line 191\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_6\n\n    .line 182\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v13\n\n    .line 183\n    :goto_7\n    # getter for: Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n    invoke-static {}, Lit/necst/grabnrun/FileDownloader;->access$0()Ljava/lang/String;\n\n    move-result-object v14\n\n    const-string v15, \"Clean up all pending streams..\"\n\n    invoke-static {v14, v15}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 184\n    if-eqz v12, :cond_b\n\n    .line 185\n    invoke-virtual {v12}, Ljava/net/HttpURLConnection;->disconnect()V\n\n    .line 187\n    :cond_b\n    if-eqz v5, :cond_c\n\n    .line 189\n    :try_start_9\n    invoke-virtual {v5}, Ljava/io/InputStream;->close()V\n    :try_end_9\n    .catch Ljava/io/IOException; {:try_start_9 .. :try_end_9} :catch_3\n\n    .line 194\n    :cond_c\n    :goto_8\n    if-eqz v7, :cond_d\n\n    .line 197\n    :try_start_a\n    invoke-virtual {v7}, Ljava/io/OutputStream;->close()V\n    :try_end_a\n    .catch Ljava/io/IOException; {:try_start_a .. :try_end_a} :catch_4\n\n    .line 202\n    :cond_d\n    :goto_9\n    throw v13\n\n    .line 190\n    :catch_3\n    move-exception v4\n\n    .line 191\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_8\n\n    .line 198\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catch_4\n    move-exception v4\n\n    .line 199\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_9\n\n    .line 190\n    .end local v4    # \"e\":Ljava/io/IOException;\n    .end local v5    # \"inputStream\":Ljava/io/InputStream;\n    .end local v7    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v1    # \"bytes\":[B\n    .restart local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v9    # \"read\":I\n    :catch_5\n    move-exception v4\n\n    .line 191\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_3\n\n    .line 198\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catch_6\n    move-exception v4\n\n    .line 199\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :cond_e\n    move-object v7, v8\n\n    .end local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v7    # \"outputStream\":Ljava/io/OutputStream;\n    move-object v5, v6\n\n    .end local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v5    # \"inputStream\":Ljava/io/InputStream;\n    goto :goto_4\n\n    .line 182\n    .end local v1    # \"bytes\":[B\n    .end local v5    # \"inputStream\":Ljava/io/InputStream;\n    .end local v9    # \"read\":I\n    .restart local v6    # \"inputStream\":Ljava/io/InputStream;\n    :catchall_1\n    move-exception v13\n\n    move-object v5, v6\n\n    .end local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v5    # \"inputStream\":Ljava/io/InputStream;\n    goto :goto_7\n\n    .end local v5    # \"inputStream\":Ljava/io/InputStream;\n    .end local v7    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v9    # \"read\":I\n    :catchall_2\n    move-exception v13\n\n    move-object v7, v8\n\n    .end local v8    # \"outputStream\":Ljava/io/OutputStream;\n    .restart local v7    # \"outputStream\":Ljava/io/OutputStream;\n    move-object v5, v6\n\n    .end local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v5    # \"inputStream\":Ljava/io/InputStream;\n    goto :goto_7\n\n    .line 177\n    .end local v9    # \"read\":I\n    :catch_7\n    move-exception v13\n\n    goto :goto_5\n\n    .end local v5    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v6    # \"inputStream\":Ljava/io/InputStream;\n    :catch_8\n    move-exception v13\n\n    move-object v5, v6\n\n    .end local v6    # \"inputStream\":Ljava/io/InputStream;\n    .restart local v5    # \"inputStream\":Ljava/io/InputStream;\n    goto :goto_5\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/FileDownloader.smali",
    "content": ".class final Lit/necst/grabnrun/FileDownloader;\n.super Ljava/lang/Object;\n.source \"FileDownloader.java\"\n\n\n# static fields\n.field private static final TAG_FILE_DOWNLOADER:Ljava/lang/String;\n\n\n# instance fields\n.field private activeNetworkInfo:Landroid/net/NetworkInfo;\n\n.field private fileMimeType:Ljava/lang/String;\n\n.field private mConnectivityManager:Landroid/net/ConnectivityManager;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 1\n\n    .prologue\n    .line 44\n    const-class v0, Lit/necst/grabnrun/FileDownloader;\n\n    invoke-virtual {v0}, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;\n\n    move-result-object v0\n\n    sput-object v0, Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n\n    return-void\n.end method\n\n.method constructor <init>(Landroid/content/ContextWrapper;)V\n    .locals 1\n    .param p1, \"parentContextWrapper\"    # Landroid/content/ContextWrapper;\n\n    .prologue\n    .line 59\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 61\n    const-string v0, \"connectivity\"\n\n    invoke-virtual {p1, v0}, Landroid/content/ContextWrapper;->getSystemService(Ljava/lang/String;)Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Landroid/net/ConnectivityManager;\n\n    iput-object v0, p0, Lit/necst/grabnrun/FileDownloader;->mConnectivityManager:Landroid/net/ConnectivityManager;\n\n    .line 62\n    const/4 v0, 0x0\n\n    iput-object v0, p0, Lit/necst/grabnrun/FileDownloader;->fileMimeType:Ljava/lang/String;\n\n    .line 63\n    return-void\n.end method\n\n.method static synthetic access$0()Ljava/lang/String;\n    .locals 1\n\n    .prologue\n    .line 44\n    sget-object v0, Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n\n    return-object v0\n.end method\n\n.method static synthetic access$1(Lit/necst/grabnrun/FileDownloader;Ljava/lang/String;)V\n    .locals 0\n\n    .prologue\n    .line 49\n    iput-object p1, p0, Lit/necst/grabnrun/FileDownloader;->fileMimeType:Ljava/lang/String;\n\n    return-void\n.end method\n\n\n# virtual methods\n.method final downloadRemoteUrl(Ljava/net/URL;Ljava/lang/String;Z)Z\n    .locals 8\n    .param p1, \"remoteURL\"    # Ljava/net/URL;\n    .param p2, \"localURI\"    # Ljava/lang/String;\n    .param p3, \"isRedirectAllowed\"    # Z\n\n    .prologue\n    const/4 v3, 0x0\n\n    .line 85\n    iget-object v4, p0, Lit/necst/grabnrun/FileDownloader;->mConnectivityManager:Landroid/net/ConnectivityManager;\n\n    invoke-virtual {v4}, Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;\n\n    move-result-object v4\n\n    iput-object v4, p0, Lit/necst/grabnrun/FileDownloader;->activeNetworkInfo:Landroid/net/NetworkInfo;\n\n    .line 86\n    iget-object v4, p0, Lit/necst/grabnrun/FileDownloader;->activeNetworkInfo:Landroid/net/NetworkInfo;\n\n    if-eqz v4, :cond_0\n\n    iget-object v4, p0, Lit/necst/grabnrun/FileDownloader;->activeNetworkInfo:Landroid/net/NetworkInfo;\n\n    invoke-virtual {v4}, Landroid/net/NetworkInfo;->isConnected()Z\n\n    move-result v4\n\n    if-nez v4, :cond_2\n\n    .line 88\n    :cond_0\n    sget-object v4, Lit/necst/grabnrun/FileDownloader;->TAG_FILE_DOWNLOADER:Ljava/lang/String;\n\n    const-string v5, \"No connectivity is available. Download failed!\"\n\n    invoke-static {v4, v5}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 223\n    :cond_1\n    :goto_0\n    return v3\n\n    .line 93\n    :cond_2\n    new-instance v0, Lit/necst/grabnrun/FileDownloader$1;\n\n    invoke-direct {v0, p0, p1, p3, p2}, Lit/necst/grabnrun/FileDownloader$1;-><init>(Lit/necst/grabnrun/FileDownloader;Ljava/net/URL;ZLjava/lang/String;)V\n\n    .line 207\n    .local v0, \"dataThread\":Ljava/lang/Thread;\n    invoke-virtual {v0}, Ljava/lang/Thread;->start()V\n\n    .line 211\n    :try_start_0\n    invoke-virtual {v0}, Ljava/lang/Thread;->join()V\n    :try_end_0\n    .catch Ljava/lang/InterruptedException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 217\n    new-instance v2, Ljava/io/File;\n\n    invoke-direct {v2, p2}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 220\n    .local v2, \"fileAtLocalURI\":Ljava/io/File;\n    invoke-virtual {v2}, Ljava/io/File;->exists()Z\n\n    move-result v4\n\n    if-eqz v4, :cond_1\n\n    invoke-virtual {v2}, Ljava/io/File;->length()J\n\n    move-result-wide v4\n\n    const-wide/16 v6, 0x0\n\n    cmp-long v4, v4, v6\n\n    if-lez v4, :cond_1\n\n    .line 221\n    const/4 v3, 0x1\n\n    goto :goto_0\n\n    .line 212\n    .end local v2    # \"fileAtLocalURI\":Ljava/io/File;\n    :catch_0\n    move-exception v1\n\n    .line 213\n    .local v1, \"e\":Ljava/lang/InterruptedException;\n    invoke-virtual {v1}, Ljava/lang/InterruptedException;->printStackTrace()V\n\n    goto :goto_0\n.end method\n\n.method getDownloadedFileExtension()Ljava/lang/String;\n    .locals 3\n\n    .prologue\n    const/4 v0, 0x0\n\n    .line 235\n    iget-object v1, p0, Lit/necst/grabnrun/FileDownloader;->fileMimeType:Ljava/lang/String;\n\n    if-nez v1, :cond_1\n\n    .line 254\n    :cond_0\n    :goto_0\n    return-object v0\n\n    .line 238\n    :cond_1\n    iget-object v1, p0, Lit/necst/grabnrun/FileDownloader;->fileMimeType:Ljava/lang/String;\n\n    invoke-virtual {v1}, Ljava/lang/String;->hashCode()I\n\n    move-result v2\n\n    sparse-switch v2, :sswitch_data_0\n\n    goto :goto_0\n\n    :sswitch_0\n    const-string v2, \"application/vnd.android.package-archive\"\n\n    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_0\n\n    .line 242\n    const-string v0, \".apk\"\n\n    goto :goto_0\n\n    .line 238\n    :sswitch_1\n    const-string v2, \"application/octet-stream\"\n\n    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_0\n\n    .line 250\n    const-string v0, \".pem\"\n\n    goto :goto_0\n\n    .line 238\n    :sswitch_2\n    const-string v2, \"application/java-archive\"\n\n    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_0\n\n    .line 246\n    const-string v0, \".jar\"\n\n    goto :goto_0\n\n    .line 238\n    nop\n\n    :sswitch_data_0\n    .sparse-switch\n        0x4d6213b -> :sswitch_0\n        0x463e3f9d -> :sswitch_1\n        0x7a257a76 -> :sswitch_2\n    .end sparse-switch\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/FileFilterByName.smali",
    "content": ".class final Lit/necst/grabnrun/FileFilterByName;\n.super Ljava/lang/Object;\n.source \"FileFilterByName.java\"\n\n# interfaces\n.implements Ljava/io/FileFilter;\n\n\n# instance fields\n.field private extension:Ljava/lang/String;\n\n.field private name:Ljava/lang/String;\n\n\n# direct methods\n.method constructor <init>(Ljava/lang/String;Ljava/lang/String;)V\n    .locals 0\n    .param p1, \"name\"    # Ljava/lang/String;\n    .param p2, \"extension\"    # Ljava/lang/String;\n\n    .prologue\n    .line 42\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 44\n    iput-object p1, p0, Lit/necst/grabnrun/FileFilterByName;->name:Ljava/lang/String;\n\n    .line 45\n    iput-object p2, p0, Lit/necst/grabnrun/FileFilterByName;->extension:Ljava/lang/String;\n\n    .line 46\n    return-void\n.end method\n\n\n# virtual methods\n.method public final accept(Ljava/io/File;)Z\n    .locals 4\n    .param p1, \"file\"    # Ljava/io/File;\n\n    .prologue\n    const/4 v0, 0x0\n\n    .line 53\n    invoke-virtual {p1}, Ljava/io/File;->isDirectory()Z\n\n    move-result v1\n\n    if-eqz v1, :cond_1\n\n    .line 65\n    :cond_0\n    :goto_0\n    return v0\n\n    .line 55\n    :cond_1\n    invoke-virtual {p1}, Ljava/io/File;->isFile()Z\n\n    move-result v1\n\n    if-eqz v1, :cond_0\n\n    .line 60\n    invoke-virtual {p1}, Ljava/io/File;->getName()Ljava/lang/String;\n\n    move-result-object v1\n\n    new-instance v2, Ljava/lang/StringBuilder;\n\n    iget-object v3, p0, Lit/necst/grabnrun/FileFilterByName;->name:Ljava/lang/String;\n\n    invoke-static {v3}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-direct {v2, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    iget-object v3, p0, Lit/necst/grabnrun/FileFilterByName;->extension:Ljava/lang/String;\n\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v2\n\n    invoke-virtual {v1, v2}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-eqz v1, :cond_0\n\n    .line 61\n    const/4 v0, 0x1\n\n    goto :goto_0\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/PackageNameTrie.smali",
    "content": ".class final Lit/necst/grabnrun/PackageNameTrie;\n.super Ljava/lang/Object;\n.source \"PackageNameTrie.java\"\n\n\n# static fields\n.field private static final TAG_PACKAGE_NAME_TRIE:Ljava/lang/String;\n\n\n# instance fields\n.field private packageNameToHasCertificateMap:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/Boolean;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 1\n\n    .prologue\n    .line 39\n    const-class v0, Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-virtual {v0}, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;\n\n    move-result-object v0\n\n    sput-object v0, Lit/necst/grabnrun/PackageNameTrie;->TAG_PACKAGE_NAME_TRIE:Ljava/lang/String;\n\n    return-void\n.end method\n\n.method constructor <init>()V\n    .locals 3\n\n    .prologue\n    .line 47\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 49\n    new-instance v0, Ljava/util/HashMap;\n\n    invoke-direct {v0}, Ljava/util/HashMap;-><init>()V\n\n    iput-object v0, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    .line 52\n    iget-object v0, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    const-string v1, \"\"\n\n    const/4 v2, 0x1\n\n    invoke-static {v2}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;\n\n    move-result-object v2\n\n    invoke-interface {v0, v1, v2}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 53\n    return-void\n.end method\n\n.method private final getUpALevel(Ljava/lang/String;)Ljava/lang/String;\n    .locals 2\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 98\n    const/16 v1, 0x2e\n\n    invoke-virtual {p1, v1}, Ljava/lang/String;->lastIndexOf(I)I\n\n    move-result v0\n\n    .line 100\n    .local v0, \"lastPointIndex\":I\n    const/4 v1, -0x1\n\n    if-eq v0, v1, :cond_0\n\n    .line 101\n    const/4 v1, 0x0\n\n    invoke-virtual {p1, v1, v0}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v1\n\n    .line 103\n    :goto_0\n    return-object v1\n\n    :cond_0\n    const-string v1, \"\"\n\n    goto :goto_0\n.end method\n\n\n# virtual methods\n.method final generateEntriesForPackageName(Ljava/lang/String;)V\n    .locals 5\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 69\n    move-object v0, p1\n\n    .line 70\n    .local v0, \"currentPackageName\":Ljava/lang/String;\n    const/4 v1, 0x0\n\n    .line 72\n    .local v1, \"hasFoundAnAlreadyInsertedPackageName\":Z\n    :goto_0\n    if-eqz v1, :cond_0\n\n    .line 92\n    return-void\n\n    .line 74\n    :cond_0\n    iget-object v2, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v2, v0}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v2\n\n    if-eqz v2, :cond_1\n\n    .line 78\n    const/4 v1, 0x1\n\n    .line 80\n    goto :goto_0\n\n    .line 83\n    :cond_1\n    iget-object v2, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    const/4 v3, 0x0\n\n    invoke-static {v3}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;\n\n    move-result-object v3\n\n    invoke-interface {v2, v0, v3}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 85\n    sget-object v2, Lit/necst/grabnrun/PackageNameTrie;->TAG_PACKAGE_NAME_TRIE:Ljava/lang/String;\n\n    new-instance v3, Ljava/lang/StringBuilder;\n\n    const-string v4, \"Inserted a new entry for \"\n\n    invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v3, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v3\n\n    invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-static {v2, v3}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 89\n    invoke-direct {p0, v0}, Lit/necst/grabnrun/PackageNameTrie;->getUpALevel(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v0\n\n    goto :goto_0\n.end method\n\n.method final getPackageNameWithAssociatedCertificate(Ljava/lang/String;)Ljava/lang/String;\n    .locals 4\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 135\n    move-object v0, p1\n\n    .line 137\n    .local v0, \"currentPackageName\":Ljava/lang/String;\n    iget-object v1, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v1, v0}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v1\n\n    if-nez v1, :cond_1\n\n    .line 138\n    const-string v1, \"\"\n\n    .line 146\n    :goto_0\n    return-object v1\n\n    .line 141\n    :cond_0\n    invoke-direct {p0, v0}, Lit/necst/grabnrun/PackageNameTrie;->getUpALevel(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v0\n\n    .line 140\n    :cond_1\n    iget-object v1, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v1, v0}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/Boolean;\n\n    invoke-virtual {v1}, Ljava/lang/Boolean;->booleanValue()Z\n\n    move-result v1\n\n    if-eqz v1, :cond_0\n\n    .line 143\n    sget-object v1, Lit/necst/grabnrun/PackageNameTrie;->TAG_PACKAGE_NAME_TRIE:Ljava/lang/String;\n\n    new-instance v2, Ljava/lang/StringBuilder;\n\n    invoke-static {v0}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-direct {v2, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v3, \" is the closest package name to the target \"\n\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    .line 144\n    invoke-virtual {v2, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    const-string v3, \" with an associated certificate for verification.\"\n\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v2\n\n    .line 143\n    invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    move-object v1, v0\n\n    .line 146\n    goto :goto_0\n.end method\n\n.method final setEntryHasAssociatedCertificate(Ljava/lang/String;)V\n    .locals 3\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 115\n    iget-object v0, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v0, p1}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    if-eqz v0, :cond_0\n\n    .line 117\n    iget-object v0, p0, Lit/necst/grabnrun/PackageNameTrie;->packageNameToHasCertificateMap:Ljava/util/Map;\n\n    const/4 v1, 0x1\n\n    invoke-static {v1}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;\n\n    move-result-object v1\n\n    invoke-interface {v0, p1, v1}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 118\n    sget-object v0, Lit/necst/grabnrun/PackageNameTrie;->TAG_PACKAGE_NAME_TRIE:Ljava/lang/String;\n\n    new-instance v1, Ljava/lang/StringBuilder;\n\n    invoke-static {p1}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v2\n\n    invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v2, \" has a certificate associated now.\"\n\n    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v1\n\n    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v1\n\n    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 120\n    :cond_0\n    return-void\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/RepackHandler.smali",
    "content": ".class public Lit/necst/grabnrun/RepackHandler;\n.super Ljava/lang/Object;\n.source \"RepackHandler.java\"\n\n\n# static fields\n.field private static activityStack:Ljava/util/List;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/List\",\n            \"<\",\n            \"Landroid/app/Activity;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field private static containerToPackageNamesMap:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;>;\"\n        }\n    .end annotation\n.end field\n\n.field private static gotUserInput:Z\n\n.field private static hasStaticAssociativeMap:Z\n\n.field private static mSecureLoaderFactory:Lit/necst/grabnrun/SecureLoaderFactory;\n\n.field private static messageDigest:Ljava/security/MessageDigest;\n\n.field private static packageNameToCertificateURLMap:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 1\n\n    .prologue\n    .line 40\n    const/4 v0, 0x0\n\n    sput-boolean v0, Lit/necst/grabnrun/RepackHandler;->gotUserInput:Z\n\n    .line 60\n    return-void\n.end method\n\n.method public constructor <init>()V\n    .locals 0\n\n    .prologue\n    .line 37\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    return-void\n.end method\n\n.method private static cleanUpFinishedActivities()V\n    .locals 3\n\n    .prologue\n    .line 76\n    sget-object v2, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    invoke-interface {v2}, Ljava/util/List;->iterator()Ljava/util/Iterator;\n\n    move-result-object v0\n\n    .line 80\n    .local v0, \"activityStackIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Landroid/app/Activity;>;\"\n    :cond_0\n    :goto_0\n    invoke-interface {v0}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v2\n\n    if-nez v2, :cond_1\n\n    .line 90\n    return-void\n\n    .line 82\n    :cond_1\n    invoke-interface {v0}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Landroid/app/Activity;\n\n    .line 84\n    .local v1, \"currentActivity\":Landroid/app/Activity;\n    if-eqz v1, :cond_2\n\n    invoke-virtual {v1}, Landroid/app/Activity;->isFinishing()Z\n\n    move-result v2\n\n    if-eqz v2, :cond_0\n\n    .line 87\n    :cond_2\n    invoke-interface {v0}, Ljava/util/Iterator;->remove()V\n\n    goto :goto_0\n.end method\n\n.method private static computeDigestEncodeFromFilePath(Ljava/lang/String;)Ljava/lang/String;\n    .locals 11\n    .param p0, \"filePath\"    # Ljava/lang/String;\n\n    .prologue\n    const/4 v10, -0x1\n\n    const/4 v8, 0x0\n\n    .line 310\n    const/4 v5, 0x0\n\n    .line 311\n    .local v5, \"inStream\":Ljava/io/FileInputStream;\n    const/4 v2, 0x0\n\n    .line 314\n    .local v2, \"digestString\":Ljava/lang/String;\n    if-eqz p0, :cond_0\n\n    new-instance v9, Ljava/io/File;\n\n    invoke-direct {v9, p0}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v9}, Ljava/io/File;->exists()Z\n\n    move-result v9\n\n    if-nez v9, :cond_1\n\n    .line 358\n    :cond_0\n    :goto_0\n    return-object v8\n\n    .line 317\n    :cond_1\n    const-string v9, \".\"\n\n    invoke-virtual {p0, v9}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v4\n\n    .line 319\n    .local v4, \"extensionIndex\":I\n    if-eq v4, v10, :cond_0\n\n    .line 321\n    invoke-virtual {p0, v4}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v3\n\n    .line 323\n    .local v3, \"extension\":Ljava/lang/String;\n    const-string v9, \".apk\"\n\n    invoke-virtual {v3, v9}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v9\n\n    if-nez v9, :cond_2\n\n    const-string v9, \".jar\"\n\n    invoke-virtual {v3, v9}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v9\n\n    if-eqz v9, :cond_0\n\n    .line 327\n    :cond_2\n    :try_start_0\n    new-instance v6, Ljava/io/FileInputStream;\n\n    invoke-direct {v6, p0}, Ljava/io/FileInputStream;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_7\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_2\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 329\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .local v6, \"inStream\":Ljava/io/FileInputStream;\n    const/16 v8, 0x2000\n\n    :try_start_1\n    new-array v0, v8, [B\n\n    .line 331\n    .local v0, \"buffer\":[B\n    :goto_1\n    invoke-virtual {v6, v0}, Ljava/io/FileInputStream;->read([B)I\n\n    move-result v7\n\n    .local v7, \"length\":I\n    if-ne v7, v10, :cond_4\n\n    .line 336\n    sget-object v8, Lit/necst/grabnrun/RepackHandler;->messageDigest:Ljava/security/MessageDigest;\n\n    invoke-virtual {v8}, Ljava/security/MessageDigest;->digest()[B\n\n    move-result-object v1\n\n    .line 340\n    .local v1, \"digestBytes\":[B\n    const/16 v8, 0x8\n\n    invoke-static {v1, v8}, Landroid/util/Base64;->encodeToString([BI)Ljava/lang/String;\n\n    move-result-object v2\n\n    .line 341\n    const-string v8, \"line.separator\"\n\n    invoke-static {v8}, Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v8\n\n    const-string v9, \"\"\n\n    invoke-virtual {v2, v8, v9}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;\n\n    move-result-object v8\n\n    const-string v9, \"\\r\"\n\n    const-string v10, \"\"\n\n    invoke-virtual {v8, v9, v10}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;\n    :try_end_1\n    .catch Ljava/io/FileNotFoundException; {:try_start_1 .. :try_end_1} :catch_0\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_6\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    move-result-object v2\n\n    .line 348\n    if-eqz v6, :cond_6\n\n    .line 350\n    :try_start_2\n    invoke-virtual {v6}, Ljava/io/FileInputStream;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_4\n\n    move-object v5, v6\n\n    .end local v0    # \"buffer\":[B\n    .end local v1    # \"digestBytes\":[B\n    .end local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .end local v7    # \"length\":I\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    :cond_3\n    :goto_2\n    move-object v8, v2\n\n    .line 358\n    goto :goto_0\n\n    .line 333\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v0    # \"buffer\":[B\n    .restart local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v7    # \"length\":I\n    :cond_4\n    :try_start_3\n    sget-object v8, Lit/necst/grabnrun/RepackHandler;->messageDigest:Ljava/security/MessageDigest;\n\n    const/4 v9, 0x0\n\n    invoke-virtual {v8, v0, v9, v7}, Ljava/security/MessageDigest;->update([BII)V\n    :try_end_3\n    .catch Ljava/io/FileNotFoundException; {:try_start_3 .. :try_end_3} :catch_0\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_6\n    .catchall {:try_start_3 .. :try_end_3} :catchall_1\n\n    goto :goto_1\n\n    .line 343\n    .end local v0    # \"buffer\":[B\n    .end local v7    # \"length\":I\n    :catch_0\n    move-exception v8\n\n    move-object v5, v6\n\n    .line 348\n    .end local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    :goto_3\n    if-eqz v5, :cond_3\n\n    .line 350\n    :try_start_4\n    invoke-virtual {v5}, Ljava/io/FileInputStream;->close()V\n    :try_end_4\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_1\n\n    goto :goto_2\n\n    .line 351\n    :catch_1\n    move-exception v8\n\n    goto :goto_2\n\n    .line 345\n    :catch_2\n    move-exception v8\n\n    .line 348\n    :goto_4\n    if-eqz v5, :cond_3\n\n    .line 350\n    :try_start_5\n    invoke-virtual {v5}, Ljava/io/FileInputStream;->close()V\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_3\n\n    goto :goto_2\n\n    .line 351\n    :catch_3\n    move-exception v8\n\n    goto :goto_2\n\n    .line 347\n    :catchall_0\n    move-exception v8\n\n    .line 348\n    :goto_5\n    if-eqz v5, :cond_5\n\n    .line 350\n    :try_start_6\n    invoke-virtual {v5}, Ljava/io/FileInputStream;->close()V\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_5\n\n    .line 355\n    :cond_5\n    :goto_6\n    throw v8\n\n    .line 351\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v0    # \"buffer\":[B\n    .restart local v1    # \"digestBytes\":[B\n    .restart local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v7    # \"length\":I\n    :catch_4\n    move-exception v8\n\n    move-object v5, v6\n\n    .end local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_2\n\n    .end local v0    # \"buffer\":[B\n    .end local v1    # \"digestBytes\":[B\n    .end local v7    # \"length\":I\n    :catch_5\n    move-exception v9\n\n    goto :goto_6\n\n    .line 347\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v6    # \"inStream\":Ljava/io/FileInputStream;\n    :catchall_1\n    move-exception v8\n\n    move-object v5, v6\n\n    .end local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_5\n\n    .line 345\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v6    # \"inStream\":Ljava/io/FileInputStream;\n    :catch_6\n    move-exception v8\n\n    move-object v5, v6\n\n    .end local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_4\n\n    .line 343\n    :catch_7\n    move-exception v8\n\n    goto :goto_3\n\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v0    # \"buffer\":[B\n    .restart local v1    # \"digestBytes\":[B\n    .restart local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v7    # \"length\":I\n    :cond_6\n    move-object v5, v6\n\n    .end local v6    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_2\n.end method\n\n.method public static enqueRunningActivity(Landroid/app/Activity;)V\n    .locals 1\n    .param p0, \"newActivity\"    # Landroid/app/Activity;\n\n    .prologue\n    .line 65\n    sget-object v0, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    if-nez v0, :cond_0\n\n    .line 66\n    new-instance v0, Ljava/util/ArrayList;\n\n    invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V\n\n    sput-object v0, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    .line 69\n    :cond_0\n    if-eqz p0, :cond_1\n\n    .line 70\n    sget-object v0, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    invoke-interface {v0, p0}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n\n    .line 71\n    :cond_1\n    return-void\n.end method\n\n.method public static generateSecureDexClassLoader(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Lit/necst/grabnrun/SecureDexClassLoader;\n    .locals 17\n    .param p0, \"dexPath\"    # Ljava/lang/String;\n    .param p1, \"optimizedDirectory\"    # Ljava/lang/String;\n    .param p2, \"libraryPath\"    # Ljava/lang/String;\n    .param p3, \"parent\"    # Ljava/lang/ClassLoader;\n\n    .prologue\n    .line 178\n    sget-boolean v13, Lit/necst/grabnrun/RepackHandler;->gotUserInput:Z\n\n    if-nez v13, :cond_0\n\n    .line 179\n    invoke-static {}, Lit/necst/grabnrun/RepackHandler;->initializeUserInput()V\n\n    .line 182\n    :cond_0\n    new-instance v7, Ljava/util/HashMap;\n\n    invoke-direct {v7}, Ljava/util/HashMap;-><init>()V\n\n    .line 184\n    .local v7, \"finalAssociativeMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    sget-boolean v13, Lit/necst/grabnrun/RepackHandler;->hasStaticAssociativeMap:Z\n\n    if-eqz v13, :cond_2\n\n    .line 188\n    sget-object v13, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;\n\n    invoke-interface {v13}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v13\n\n    invoke-interface {v13}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v13\n\n    invoke-static {v7, v13}, Lit/necst/grabnrun/RepackHandler;->insertURLEntriesInMap(Ljava/util/Map;Ljava/util/Iterator;)V\n\n    .line 277\n    :cond_1\n    new-instance v13, Lit/necst/grabnrun/SecureLoaderFactory;\n\n    invoke-static {}, Lit/necst/grabnrun/RepackHandler;->getLastRunningActivity()Landroid/app/Activity;\n\n    move-result-object v14\n\n    invoke-direct {v13, v14}, Lit/necst/grabnrun/SecureLoaderFactory;-><init>(Landroid/content/ContextWrapper;)V\n\n    sput-object v13, Lit/necst/grabnrun/RepackHandler;->mSecureLoaderFactory:Lit/necst/grabnrun/SecureLoaderFactory;\n\n    .line 280\n    const-string v13, \"Repack Handler\"\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"Original dexPath: \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, p0\n\n    invoke-virtual {v14, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 281\n    const-string v13, \"Repack Handler\"\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"Optimized cached dex directory: \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, p1\n\n    invoke-virtual {v14, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 284\n    sget-object v13, Lit/necst/grabnrun/RepackHandler;->mSecureLoaderFactory:Lit/necst/grabnrun/SecureLoaderFactory;\n\n    move-object/from16 v0, p0\n\n    move-object/from16 v1, p2\n\n    move-object/from16 v2, p3\n\n    invoke-virtual {v13, v0, v1, v2, v7}, Lit/necst/grabnrun/SecureLoaderFactory;->createDexClassLoader(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/Map;)Lit/necst/grabnrun/SecureDexClassLoader;\n\n    move-result-object v13\n\n    return-object v13\n\n    .line 197\n    :cond_2\n    const-string v13, \"http://\"\n\n    const-string v14, \"http//\"\n\n    move-object/from16 v0, p0\n\n    invoke-virtual {v0, v13, v14}, Ljava/lang/String;->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v12\n\n    .line 198\n    .local v12, \"tempPath\":Ljava/lang/String;\n    const-string v13, \"https://\"\n\n    const-string v14, \"https//\"\n\n    invoke-virtual {v12, v13, v14}, Ljava/lang/String;->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v12\n\n    .line 201\n    sget-object v13, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-static {v13}, Ljava/util/regex/Pattern;->quote(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v13\n\n    invoke-virtual {v12, v13}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;\n\n    move-result-object v11\n\n    .line 203\n    .local v11, \"strings\":[Ljava/lang/String;\n    array-length v15, v11\n\n    const/4 v13, 0x0\n\n    move v14, v13\n\n    :goto_0\n    if-ge v14, v15, :cond_1\n\n    aget-object v10, v11, v14\n\n    .line 209\n    .local v10, \"path\":Ljava/lang/String;\n    const-string v13, \"http//\"\n\n    invoke-virtual {v10, v13}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_3\n\n    const-string v13, \"https//\"\n\n    invoke-virtual {v10, v13}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_6\n\n    .line 213\n    :cond_3\n    const-string v13, \"http//\"\n\n    invoke-virtual {v10, v13}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_5\n\n    .line 214\n    new-instance v13, Ljava/lang/StringBuilder;\n\n    const-string v16, \"http:\"\n\n    move-object/from16 v0, v16\n\n    invoke-direct {v13, v0}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const/16 v16, 0x4\n\n    move/from16 v0, v16\n\n    invoke-virtual {v10, v0}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v16\n\n    move-object/from16 v0, v16\n\n    invoke-virtual {v13, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v13\n\n    invoke-virtual {v13}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v3\n\n    .line 226\n    .local v3, \"containerIdentifier\":Ljava/lang/String;\n    :goto_1\n    if-eqz v3, :cond_4\n\n    .line 229\n    sget-object v13, Lit/necst/grabnrun/RepackHandler;->containerToPackageNamesMap:Ljava/util/Map;\n\n    invoke-interface {v13, v3}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_7\n\n    .line 232\n    sget-object v13, Lit/necst/grabnrun/RepackHandler;->containerToPackageNamesMap:Ljava/util/Map;\n\n    invoke-interface {v13, v3}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v8\n\n    check-cast v8, Ljava/util/Set;\n\n    .line 235\n    .local v8, \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    invoke-interface {v8}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v13\n\n    invoke-static {v7, v13}, Lit/necst/grabnrun/RepackHandler;->insertURLEntriesInMap(Ljava/util/Map;Ljava/util/Iterator;)V\n\n    .line 203\n    .end local v8    # \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :cond_4\n    :goto_2\n    add-int/lit8 v13, v14, 0x1\n\n    move v14, v13\n\n    goto :goto_0\n\n    .line 216\n    .end local v3    # \"containerIdentifier\":Ljava/lang/String;\n    :cond_5\n    new-instance v13, Ljava/lang/StringBuilder;\n\n    const-string v16, \"https:\"\n\n    move-object/from16 v0, v16\n\n    invoke-direct {v13, v0}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const/16 v16, 0x5\n\n    move/from16 v0, v16\n\n    invoke-virtual {v10, v0}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v16\n\n    move-object/from16 v0, v16\n\n    invoke-virtual {v13, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v13\n\n    invoke-virtual {v13}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v3\n\n    .line 218\n    .restart local v3    # \"containerIdentifier\":Ljava/lang/String;\n    goto :goto_1\n\n    .line 222\n    .end local v3    # \"containerIdentifier\":Ljava/lang/String;\n    :cond_6\n    invoke-static {v10}, Lit/necst/grabnrun/RepackHandler;->computeDigestEncodeFromFilePath(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v3\n\n    .restart local v3    # \"containerIdentifier\":Ljava/lang/String;\n    goto :goto_1\n\n    .line 241\n    :cond_7\n    sget-object v13, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;\n\n    const-string v16, \"default\"\n\n    move-object/from16 v0, v16\n\n    invoke-interface {v13, v0}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_4\n\n    .line 243\n    sget-object v13, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;\n\n    const-string v16, \"default\"\n\n    move-object/from16 v0, v16\n\n    invoke-interface {v13, v0}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v4\n\n    check-cast v4, Ljava/lang/String;\n\n    .line 248\n    .local v4, \"defaultRemoteCert\":Ljava/lang/String;\n    :try_start_0\n    new-instance v5, Ljava/net/URL;\n\n    invoke-direct {v5, v4}, Ljava/net/URL;-><init>(Ljava/lang/String;)V\n\n    .line 252\n    .local v5, \"defaultRemoteCertURL\":Ljava/net/URL;\n    invoke-static {v10}, Lit/necst/grabnrun/RepackHandler;->getPackageNamesFromContainerPath(Ljava/lang/String;)Ljava/util/Set;\n\n    move-result-object v8\n\n    .line 254\n    .restart local v8    # \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    if-eqz v8, :cond_4\n\n    .line 256\n    invoke-interface {v8}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v9\n\n    .line 258\n    .local v9, \"packageNamesSetIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :goto_3\n    invoke-interface {v9}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v13\n\n    if-eqz v13, :cond_4\n\n    .line 262\n    invoke-interface {v9}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v13\n\n    check-cast v13, Ljava/lang/String;\n\n    invoke-interface {v7, v13, v5}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n    :try_end_0\n    .catch Ljava/net/MalformedURLException; {:try_start_0 .. :try_end_0} :catch_0\n\n    goto :goto_3\n\n    .line 266\n    .end local v5    # \"defaultRemoteCertURL\":Ljava/net/URL;\n    .end local v8    # \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    .end local v9    # \"packageNamesSetIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :catch_0\n    move-exception v6\n\n    .line 268\n    .local v6, \"e\":Ljava/net/MalformedURLException;\n    invoke-virtual {v6}, Ljava/net/MalformedURLException;->printStackTrace()V\n\n    goto :goto_2\n.end method\n\n.method private static getLastRunningActivity()Landroid/app/Activity;\n    .locals 2\n\n    .prologue\n    const/4 v0, 0x0\n\n    .line 94\n    sget-object v1, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    if-eqz v1, :cond_0\n\n    sget-object v1, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    invoke-interface {v1}, Ljava/util/List;->isEmpty()Z\n\n    move-result v1\n\n    if-eqz v1, :cond_1\n\n    .line 104\n    :cond_0\n    :goto_0\n    return-object v0\n\n    .line 98\n    :cond_1\n    invoke-static {}, Lit/necst/grabnrun/RepackHandler;->cleanUpFinishedActivities()V\n\n    .line 100\n    sget-object v1, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    invoke-interface {v1}, Ljava/util/List;->isEmpty()Z\n\n    move-result v1\n\n    if-nez v1, :cond_0\n\n    .line 104\n    sget-object v0, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    sget-object v1, Lit/necst/grabnrun/RepackHandler;->activityStack:Ljava/util/List;\n\n    invoke-interface {v1}, Ljava/util/List;->size()I\n\n    move-result v1\n\n    add-int/lit8 v1, v1, -0x1\n\n    invoke-interface {v0, v1}, Ljava/util/List;->get(I)Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Landroid/app/Activity;\n\n    goto :goto_0\n.end method\n\n.method private static getPackageNamesFromContainerPath(Ljava/lang/String;)Ljava/util/Set;\n    .locals 20\n    .param p0, \"containerPath\"    # Ljava/lang/String;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \")\",\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n\n    .prologue\n    .line 369\n    if-eqz p0, :cond_0\n\n    invoke-virtual/range {p0 .. p0}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v18\n\n    if-nez v18, :cond_0\n\n    new-instance v18, Ljava/io/File;\n\n    move-object/from16 v0, v18\n\n    move-object/from16 v1, p0\n\n    invoke-direct {v0, v1}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual/range {v18 .. v18}, Ljava/io/File;->exists()Z\n\n    move-result v18\n\n    if-nez v18, :cond_1\n\n    :cond_0\n    const/16 v17, 0x0\n\n    .line 490\n    :goto_0\n    return-object v17\n\n    .line 372\n    :cond_1\n    const-string v18, \".\"\n\n    move-object/from16 v0, p0\n\n    move-object/from16 v1, v18\n\n    invoke-virtual {v0, v1}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v8\n\n    .line 374\n    .local v8, \"extensionIndex\":I\n    const/16 v18, -0x1\n\n    move/from16 v0, v18\n\n    if-ne v8, v0, :cond_2\n\n    const/16 v17, 0x0\n\n    goto :goto_0\n\n    .line 376\n    :cond_2\n    move-object/from16 v0, p0\n\n    invoke-virtual {v0, v8}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v7\n\n    .line 378\n    .local v7, \"extension\":Ljava/lang/String;\n    new-instance v17, Ljava/util/HashSet;\n\n    invoke-direct/range {v17 .. v17}, Ljava/util/HashSet;-><init>()V\n\n    .line 380\n    .local v17, \"packageNameSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    invoke-static {}, Lit/necst/grabnrun/RepackHandler;->getLastRunningActivity()Landroid/app/Activity;\n\n    move-result-object v12\n\n    .line 382\n    .local v12, \"lastRunningActivity\":Landroid/content/ContextWrapper;\n    if-nez v12, :cond_3\n\n    const/16 v17, 0x0\n\n    goto :goto_0\n\n    .line 384\n    :cond_3\n    const-string v18, \".apk\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v7, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v18\n\n    if-eqz v18, :cond_5\n\n    .line 388\n    invoke-virtual {v12}, Landroid/content/ContextWrapper;->getPackageManager()Landroid/content/pm/PackageManager;\n\n    move-result-object v13\n\n    .line 390\n    .local v13, \"mPackageManager\":Landroid/content/pm/PackageManager;\n    const/16 v18, 0x0\n\n    move-object/from16 v0, p0\n\n    move/from16 v1, v18\n\n    invoke-virtual {v13, v0, v1}, Landroid/content/pm/PackageManager;->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;\n\n    move-result-object v18\n\n    if-eqz v18, :cond_4\n\n    .line 392\n    const/16 v18, 0x0\n\n    move-object/from16 v0, p0\n\n    move/from16 v1, v18\n\n    invoke-virtual {v13, v0, v1}, Landroid/content/pm/PackageManager;->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;\n\n    move-result-object v18\n\n    move-object/from16 v0, v18\n\n    iget-object v0, v0, Landroid/content/pm/PackageInfo;->packageName:Ljava/lang/String;\n\n    move-object/from16 v18, v0\n\n    invoke-interface/range {v17 .. v18}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n\n    goto :goto_0\n\n    .line 396\n    :cond_4\n    const/16 v17, 0x0\n\n    goto :goto_0\n\n    .line 399\n    .end local v13    # \"mPackageManager\":Landroid/content/pm/PackageManager;\n    :cond_5\n    const-string v18, \".jar\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v7, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v18\n\n    if-eqz v18, :cond_e\n\n    .line 406\n    const/4 v10, 0x0\n\n    .line 407\n    .local v10, \"isAValidJar\":Z\n    const/4 v2, 0x0\n\n    .line 412\n    .local v2, \"containerJar\":Ljava/util/jar/JarFile;\n    :try_start_0\n    new-instance v3, Ljava/util/jar/JarFile;\n\n    move-object/from16 v0, p0\n\n    invoke-direct {v3, v0}, Ljava/util/jar/JarFile;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_1\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 415\n    .end local v2    # \"containerJar\":Ljava/util/jar/JarFile;\n    .local v3, \"containerJar\":Ljava/util/jar/JarFile;\n    :try_start_1\n    const-string v18, \"classes.dex\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v3, v0}, Ljava/util/jar/JarFile;->getJarEntry(Ljava/lang/String;)Ljava/util/jar/JarEntry;\n    :try_end_1\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_5\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    move-result-object v18\n\n    if-eqz v18, :cond_6\n\n    .line 416\n    const/4 v10, 0x1\n\n    .line 421\n    :cond_6\n    if-eqz v3, :cond_7\n\n    .line 423\n    :try_start_2\n    invoke-virtual {v3}, Ljava/util/jar/JarFile;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_4\n\n    .line 429\n    :cond_7\n    :goto_1\n    if-eqz v10, :cond_d\n\n    .line 432\n    const/4 v5, 0x0\n\n    .line 442\n    .local v5, \"dexFile\":Ldalvik/system/DexFile;\n    :try_start_3\n    const-string v18, \"packagesExtractor\"\n\n    const/16 v19, 0x0\n\n    move-object/from16 v0, v18\n\n    move/from16 v1, v19\n\n    invoke-virtual {v12, v0, v1}, Landroid/content/ContextWrapper;->getDir(Ljava/lang/String;I)Ljava/io/File;\n\n    move-result-object v15\n\n    .line 445\n    .local v15, \"packageExtractFolder\":Ljava/io/File;\n    new-instance v18, Ljava/lang/StringBuilder;\n\n    invoke-virtual {v15}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v19\n\n    invoke-static/range {v19 .. v19}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v19\n\n    invoke-direct/range {v18 .. v19}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v19, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual/range {v18 .. v19}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v18\n\n    const-string v19, \"container.odex\"\n\n    invoke-virtual/range {v18 .. v19}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v18\n\n    invoke-virtual/range {v18 .. v18}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    .line 448\n    .local v14, \"outputDexTempPath\":Ljava/lang/String;\n    const/16 v18, 0x0\n\n    move-object/from16 v0, p0\n\n    move/from16 v1, v18\n\n    invoke-static {v0, v14, v1}, Ldalvik/system/DexFile;->loadDex(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;\n\n    move-result-object v5\n\n    .line 450\n    invoke-virtual {v5}, Ldalvik/system/DexFile;->entries()Ljava/util/Enumeration;\n\n    move-result-object v4\n\n    .line 452\n    .local v4, \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    :cond_8\n    :goto_2\n    invoke-interface {v4}, Ljava/util/Enumeration;->hasMoreElements()Z\n\n    move-result v18\n\n    if-nez v18, :cond_b\n\n    .line 474\n    new-instance v18, Ljava/io/File;\n\n    move-object/from16 v0, v18\n\n    invoke-direct {v0, v14}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual/range {v18 .. v18}, Ljava/io/File;->delete()Z\n    :try_end_3\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_0\n\n    goto/16 :goto_0\n\n    .line 476\n    .end local v4    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .end local v14    # \"outputDexTempPath\":Ljava/lang/String;\n    .end local v15    # \"packageExtractFolder\":Ljava/io/File;\n    :catch_0\n    move-exception v6\n\n    .line 478\n    .local v6, \"e\":Ljava/io/IOException;\n    const/16 v17, 0x0\n\n    goto/16 :goto_0\n\n    .line 418\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .end local v5    # \"dexFile\":Ldalvik/system/DexFile;\n    .end local v6    # \"e\":Ljava/io/IOException;\n    .restart local v2    # \"containerJar\":Ljava/util/jar/JarFile;\n    :catch_1\n    move-exception v6\n\n    .line 421\n    .restart local v6    # \"e\":Ljava/io/IOException;\n    :goto_3\n    if-eqz v2, :cond_9\n\n    .line 423\n    :try_start_4\n    invoke-virtual {v2}, Ljava/util/jar/JarFile;->close()V\n    :try_end_4\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_2\n\n    .line 419\n    :cond_9\n    :goto_4\n    const/16 v17, 0x0\n\n    goto/16 :goto_0\n\n    .line 424\n    :catch_2\n    move-exception v6\n\n    .line 425\n    invoke-virtual {v6}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_4\n\n    .line 420\n    .end local v6    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v18\n\n    .line 421\n    :goto_5\n    if-eqz v2, :cond_a\n\n    .line 423\n    :try_start_5\n    invoke-virtual {v2}, Ljava/util/jar/JarFile;->close()V\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_3\n\n    .line 427\n    :cond_a\n    :goto_6\n    throw v18\n\n    .line 424\n    :catch_3\n    move-exception v6\n\n    .line 425\n    .restart local v6    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v6}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_6\n\n    .line 424\n    .end local v2    # \"containerJar\":Ljava/util/jar/JarFile;\n    .end local v6    # \"e\":Ljava/io/IOException;\n    .restart local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    :catch_4\n    move-exception v6\n\n    .line 425\n    .restart local v6    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v6}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_1\n\n    .line 455\n    .end local v6    # \"e\":Ljava/io/IOException;\n    .restart local v4    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .restart local v5    # \"dexFile\":Ldalvik/system/DexFile;\n    .restart local v14    # \"outputDexTempPath\":Ljava/lang/String;\n    .restart local v15    # \"packageExtractFolder\":Ljava/io/File;\n    :cond_b\n    :try_start_6\n    invoke-interface {v4}, Ljava/util/Enumeration;->nextElement()Ljava/lang/Object;\n\n    move-result-object v9\n\n    check-cast v9, Ljava/lang/String;\n\n    .line 460\n    .local v9, \"fullClassName\":Ljava/lang/String;\n    :goto_7\n    const-string v18, \" \"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v9, v0}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v18\n\n    if-nez v18, :cond_c\n\n    .line 463\n    const-string v18, \".\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v9, v0}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v11\n\n    .line 465\n    .local v11, \"lastIndexPackageName\":I\n    const/16 v18, -0x1\n\n    move/from16 v0, v18\n\n    if-eq v11, v0, :cond_8\n\n    .line 467\n    const/16 v18, 0x0\n\n    move/from16 v0, v18\n\n    invoke-virtual {v9, v0, v11}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v16\n\n    .line 468\n    .local v16, \"packageName\":Ljava/lang/String;\n    move-object/from16 v0, v17\n\n    move-object/from16 v1, v16\n\n    invoke-interface {v0, v1}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n\n    goto :goto_2\n\n    .line 461\n    .end local v11    # \"lastIndexPackageName\":I\n    .end local v16    # \"packageName\":Ljava/lang/String;\n    :cond_c\n    const/16 v18, 0x1\n\n    invoke-virtual {v9}, Ljava/lang/String;->length()I\n\n    move-result v19\n\n    move/from16 v0, v18\n\n    move/from16 v1, v19\n\n    invoke-virtual {v9, v0, v1}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_0\n\n    move-result-object v9\n\n    goto :goto_7\n\n    .line 485\n    .end local v4    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .end local v5    # \"dexFile\":Ldalvik/system/DexFile;\n    .end local v9    # \"fullClassName\":Ljava/lang/String;\n    .end local v14    # \"outputDexTempPath\":Ljava/lang/String;\n    .end local v15    # \"packageExtractFolder\":Ljava/io/File;\n    :cond_d\n    const/16 v17, 0x0\n\n    goto/16 :goto_0\n\n    .line 490\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .end local v10    # \"isAValidJar\":Z\n    :cond_e\n    const/16 v17, 0x0\n\n    goto/16 :goto_0\n\n    .line 420\n    .restart local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v10    # \"isAValidJar\":Z\n    :catchall_1\n    move-exception v18\n\n    move-object v2, v3\n\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v2    # \"containerJar\":Ljava/util/jar/JarFile;\n    goto :goto_5\n\n    .line 418\n    .end local v2    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    :catch_5\n    move-exception v6\n\n    move-object v2, v3\n\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v2    # \"containerJar\":Ljava/util/jar/JarFile;\n    goto :goto_3\n.end method\n\n.method private static initializeUserInput()V\n    .locals 5\n\n    .prologue\n    .line 112\n    new-instance v2, Ljava/util/HashMap;\n\n    invoke-direct {v2}, Ljava/util/HashMap;-><init>()V\n\n    sput-object v2, Lit/necst/grabnrun/RepackHandler;->containerToPackageNamesMap:Ljava/util/Map;\n\n    .line 113\n    new-instance v2, Ljava/util/HashMap;\n\n    invoke-direct {v2}, Ljava/util/HashMap;-><init>()V\n\n    sput-object v2, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;\n\n    .line 116\n    new-instance v1, Ljava/util/HashSet;\n\n    invoke-direct {v1}, Ljava/util/HashSet;-><init>()V\n\n    .line 119\n    .local v1, \"packageNamesSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/RepackHandlerTail.smali",
    "content": "    sput-boolean v2, Lit/necst/grabnrun/RepackHandler;->hasStaticAssociativeMap:Z\n\n    .line 160\n    :try_start_0\n    const-string v2, \"SHA-1\"\n\n    invoke-static {v2}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;\n\n    move-result-object v2\n\n    sput-object v2, Lit/necst/grabnrun/RepackHandler;->messageDigest:Ljava/security/MessageDigest;\n    :try_end_0\n    .catch Ljava/security/NoSuchAlgorithmException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 167\n    :goto_0\n    const/4 v2, 0x1\n\n    sput-boolean v2, Lit/necst/grabnrun/RepackHandler;->gotUserInput:Z\n\n    .line 169\n    return-void\n\n    .line 161\n    :catch_0\n    move-exception v0\n\n    .line 163\n    .local v0, \"e\":Ljava/security/NoSuchAlgorithmException;\n    invoke-virtual {v0}, Ljava/security/NoSuchAlgorithmException;->printStackTrace()V\n\n    goto :goto_0\n.end method\n\n.method private static insertURLEntriesInMap(Ljava/util/Map;Ljava/util/Iterator;)V\n    .locals 4\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;\",\n            \"Ljava/util/Iterator\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;)V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 290\n    .local p0, \"associativeMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    .local p1, \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_0\n    :goto_0\n    invoke-interface {p1}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v2\n\n    if-nez v2, :cond_1\n\n    .line 305\n    return-void\n\n    .line 292\n    :cond_1\n    invoke-interface {p1}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/String;\n\n    .line 294\n    .local v1, \"packageNameToInsert\":Ljava/lang/String;\n    sget-object v2, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;\n\n    invoke-interface {v2, v1}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v2\n\n    if-eqz v2, :cond_0\n\n    .line 298\n    :try_start_0\n    new-instance v3, Ljava/net/URL;\n\n    sget-object v2, Lit/necst/grabnrun/RepackHandler;->packageNameToCertificateURLMap:Ljava/util/Map;\n\n    invoke-interface {v2, v1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v2\n\n    check-cast v2, Ljava/lang/String;\n\n    invoke-direct {v3, v2}, Ljava/net/URL;-><init>(Ljava/lang/String;)V\n\n    invoke-interface {p0, v1, v3}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n    :try_end_0\n    .catch Ljava/net/MalformedURLException; {:try_start_0 .. :try_end_0} :catch_0\n\n    goto :goto_0\n\n    .line 299\n    :catch_0\n    move-exception v0\n\n    .line 301\n    .local v0, \"e\":Ljava/net/MalformedURLException;\n    invoke-virtual {v0}, Ljava/net/MalformedURLException;->printStackTrace()V\n\n    goto :goto_0\n.end method\n\n.method public static raiseSecurityException()V\n    .locals 2\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/ClassNotFoundException;\n        }\n    .end annotation\n\n    .prologue\n    .line 363\n    new-instance v0, Ljava/lang/ClassNotFoundException;\n\n    const-string v1, \"A security constraint was violated and so SecureDexClassLoader prevents target class from being loaded.\"\n\n    invoke-direct {v0, v1}, Ljava/lang/ClassNotFoundException;-><init>(Ljava/lang/String;)V\n\n    throw v0\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/SecureDexClassLoader$SignatureVerificationTask.smali",
    "content": ".class Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;\n.super Ljava/lang/Object;\n.source \"SecureDexClassLoader.java\"\n\n# interfaces\n.implements Ljava/lang/Runnable;\n\n\n# annotations\n.annotation system Ldalvik/annotation/EnclosingClass;\n    value = Lit/necst/grabnrun/SecureDexClassLoader;\n.end annotation\n\n.annotation system Ldalvik/annotation/InnerClass;\n    accessFlags = 0x0\n    name = \"SignatureVerificationTask\"\n.end annotation\n\n\n# instance fields\n.field private containerPath:Ljava/lang/String;\n\n.field private rootPackageNameWithCertificate:Ljava/lang/String;\n\n.field private successVerifiedContainerSet:Ljava/util/Set;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field final synthetic this$0:Lit/necst/grabnrun/SecureDexClassLoader;\n\n\n# direct methods\n.method public constructor <init>(Lit/necst/grabnrun/SecureDexClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V\n    .locals 0\n    .param p2, \"containerPath\"    # Ljava/lang/String;\n    .param p3, \"rootPackageNameWithCertificate\"    # Ljava/lang/String;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;)V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 665\n    .local p4, \"successVerifiedContainerSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    iput-object p1, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->this$0:Lit/necst/grabnrun/SecureDexClassLoader;\n\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 668\n    iput-object p2, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->containerPath:Ljava/lang/String;\n\n    .line 669\n    iput-object p3, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->rootPackageNameWithCertificate:Ljava/lang/String;\n\n    .line 670\n    iput-object p4, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->successVerifiedContainerSet:Ljava/util/Set;\n\n    .line 671\n    return-void\n.end method\n\n\n# virtual methods\n.method public run()V\n    .locals 5\n\n    .prologue\n    .line 677\n    const/4 v2, 0x0\n\n    invoke-static {v2}, Landroid/os/Process;->setThreadPriority(I)V\n\n    .line 683\n    iget-object v2, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->this$0:Lit/necst/grabnrun/SecureDexClassLoader;\n\n    iget-object v3, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->rootPackageNameWithCertificate:Ljava/lang/String;\n\n    # invokes: Lit/necst/grabnrun/SecureDexClassLoader;->importCertificateFromPackageName(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n    invoke-static {v2, v3}, Lit/necst/grabnrun/SecureDexClassLoader;->access$0(Lit/necst/grabnrun/SecureDexClassLoader;Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n\n    move-result-object v1\n\n    .line 685\n    .local v1, \"verifiedCertificate\":Ljava/security/cert/X509Certificate;\n    if-eqz v1, :cond_0\n\n    .line 691\n    iget-object v2, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->this$0:Lit/necst/grabnrun/SecureDexClassLoader;\n\n    iget-object v3, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->containerPath:Ljava/lang/String;\n\n    # invokes: Lit/necst/grabnrun/SecureDexClassLoader;->verifyContainerSignatureAgainstCertificate(Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n    invoke-static {v2, v3, v1}, Lit/necst/grabnrun/SecureDexClassLoader;->access$1(Lit/necst/grabnrun/SecureDexClassLoader;Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n\n    move-result v0\n\n    .line 694\n    .local v0, \"signatureCheckIsSuccessful\":Z\n    if-eqz v0, :cond_0\n\n    .line 698\n    iget-object v3, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->successVerifiedContainerSet:Ljava/util/Set;\n\n    monitor-enter v3\n\n    .line 700\n    :try_start_0\n    iget-object v2, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->successVerifiedContainerSet:Ljava/util/Set;\n\n    iget-object v4, p0, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;->containerPath:Ljava/lang/String;\n\n    invoke-interface {v2, v4}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n\n    .line 698\n    monitor-exit v3\n\n    .line 704\n    .end local v0    # \"signatureCheckIsSuccessful\":Z\n    :cond_0\n    return-void\n\n    .line 698\n    .restart local v0    # \"signatureCheckIsSuccessful\":Z\n    :catchall_0\n    move-exception v2\n\n    monitor-exit v3\n    :try_end_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    throw v2\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/SecureDexClassLoader.smali",
    "content": ".class public Lit/necst/grabnrun/SecureDexClassLoader;\n.super Ljava/lang/Object;\n.source \"SecureDexClassLoader.java\"\n\n\n# annotations\n.annotation system Ldalvik/annotation/MemberClasses;\n    value = {\n        Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;\n    }\n.end annotation\n\n\n# static fields\n.field private static final CERTIFICATE_DIR:Ljava/lang/String; = \"valid_certs\"\n\n.field private static final KEEP_ALIVE_TIME_UNIT:Ljava/util/concurrent/TimeUnit;\n\n.field private static final MINIMUM_NUMBER_OF_CONTAINERS_FOR_CONCURRENT_VERIFICATION:I = 0x2\n\n.field private static final TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n\n# instance fields\n.field private certificateFactory:Ljava/security/cert/CertificateFactory;\n\n.field private certificateFolder:Ljava/io/File;\n\n.field private hasBeenWipedOut:Z\n\n.field private lazyAlreadyVerifiedPackageNameSet:Ljava/util/Set;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field private mDexClassLoader:Ldalvik/system/DexClassLoader;\n\n.field private mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n.field private mPackageManager:Landroid/content/pm/PackageManager;\n\n.field private mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n.field private packageNameToCertificateMap:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field private packageNameToContainerPathMap:Ljava/util/Map;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n.end field\n\n.field private performLazyEvaluation:Z\n\n.field private resDownloadFolder:Ljava/io/File;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 1\n\n    .prologue\n    .line 103\n    const-class v0, Lit/necst/grabnrun/SecureDexClassLoader;\n\n    invoke-virtual {v0}, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;\n\n    move-result-object v0\n\n    sput-object v0, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    .line 128\n    sget-object v0, Ljava/util/concurrent/TimeUnit;->MILLISECONDS:Ljava/util/concurrent/TimeUnit;\n\n    sput-object v0, Lit/necst/grabnrun/SecureDexClassLoader;->KEEP_ALIVE_TIME_UNIT:Ljava/util/concurrent/TimeUnit;\n\n    return-void\n.end method\n\n.method constructor <init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Landroid/content/ContextWrapper;Z)V\n    .locals 16\n    .param p1, \"dexPath\"    # Ljava/lang/String;\n    .param p2, \"optimizedDirectory\"    # Ljava/lang/String;\n    .param p3, \"libraryPath\"    # Ljava/lang/String;\n    .param p4, \"parent\"    # Ljava/lang/ClassLoader;\n    .param p5, \"parentContextWrapper\"    # Landroid/content/ContextWrapper;\n    .param p6, \"performLazyEvaluation\"    # Z\n\n    .prologue\n    .line 149\n    invoke-direct/range {p0 .. p0}, Ljava/lang/Object;-><init>()V\n\n    .line 155\n    new-instance v10, Ldalvik/system/DexClassLoader;\n\n    move-object/from16 v0, p1\n\n    move-object/from16 v1, p2\n\n    move-object/from16 v2, p3\n\n    move-object/from16 v3, p4\n\n    invoke-direct {v10, v0, v1, v2, v3}, Ldalvik/system/DexClassLoader;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mDexClassLoader:Ldalvik/system/DexClassLoader;\n\n    .line 157\n    const-string v10, \"valid_certs\"\n\n    const/4 v11, 0x0\n\n    move-object/from16 v0, p5\n\n    invoke-virtual {v0, v10, v11}, Landroid/content/ContextWrapper;->getDir(Ljava/lang/String;I)Ljava/io/File;\n\n    move-result-object v10\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFolder:Ljava/io/File;\n\n    .line 158\n    const-string v10, \"imported_cont\"\n\n    const/4 v11, 0x0\n\n    move-object/from16 v0, p5\n\n    invoke-virtual {v0, v10, v11}, Landroid/content/ContextWrapper;->getDir(Ljava/lang/String;I)Ljava/io/File;\n\n    move-result-object v10\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->resDownloadFolder:Ljava/io/File;\n\n    .line 161\n    invoke-virtual/range {p5 .. p5}, Landroid/content/ContextWrapper;->getPackageManager()Landroid/content/pm/PackageManager;\n\n    move-result-object v10\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageManager:Landroid/content/pm/PackageManager;\n\n    .line 163\n    new-instance v10, Lit/necst/grabnrun/FileDownloader;\n\n    move-object/from16 v0, p5\n\n    invoke-direct {v10, v0}, Lit/necst/grabnrun/FileDownloader;-><init>(Landroid/content/ContextWrapper;)V\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n    .line 165\n    const/4 v10, 0x0\n\n    move-object/from16 v0, p0\n\n    iput-boolean v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->hasBeenWipedOut:Z\n\n    .line 167\n    move/from16 v0, p6\n\n    move-object/from16 v1, p0\n\n    iput-boolean v0, v1, Lit/necst/grabnrun/SecureDexClassLoader;->performLazyEvaluation:Z\n\n    .line 169\n    new-instance v10, Ljava/util/HashSet;\n\n    invoke-direct {v10}, Ljava/util/HashSet;-><init>()V\n\n    invoke-static {v10}, Ljava/util/Collections;->synchronizedSet(Ljava/util/Set;)Ljava/util/Set;\n\n    move-result-object v10\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->lazyAlreadyVerifiedPackageNameSet:Ljava/util/Set;\n\n    .line 171\n    new-instance v10, Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-direct {v10}, Lit/necst/grabnrun/PackageNameTrie;-><init>()V\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    .line 175\n    :try_start_0\n    const-string v10, \"X.509\"\n\n    invoke-static {v10}, Ljava/security/cert/CertificateFactory;->getInstance(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;\n\n    move-result-object v10\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFactory:Ljava/security/cert/CertificateFactory;\n    :try_end_0\n    .catch Ljava/security/cert/CertificateException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 181\n    :goto_0\n    new-instance v10, Ljava/util/LinkedHashMap;\n\n    invoke-direct {v10}, Ljava/util/LinkedHashMap;-><init>()V\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    .line 183\n    new-instance v10, Ljava/util/LinkedHashMap;\n\n    invoke-direct {v10}, Ljava/util/LinkedHashMap;-><init>()V\n\n    invoke-static {v10}, Ljava/util/Collections;->synchronizedMap(Ljava/util/Map;)Ljava/util/Map;\n\n    move-result-object v10\n\n    move-object/from16 v0, p0\n\n    iput-object v10, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    .line 187\n    sget-object v10, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-static {v10}, Ljava/util/regex/Pattern;->quote(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v10\n\n    move-object/from16 v0, p1\n\n    invoke-virtual {v0, v10}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;\n\n    move-result-object v8\n\n    .line 189\n    .local v8, \"pathStrings\":[Ljava/lang/String;\n    array-length v11, v8\n\n    const/4 v10, 0x0\n\n    :goto_1\n    if-lt v10, v11, :cond_0\n\n    .line 217\n    return-void\n\n    .line 176\n    .end local v8    # \"pathStrings\":[Ljava/lang/String;\n    :catch_0\n    move-exception v5\n\n    .line 177\n    .local v5, \"e\":Ljava/security/cert/CertificateException;\n    invoke-virtual {v5}, Ljava/security/cert/CertificateException;->printStackTrace()V\n\n    goto :goto_0\n\n    .line 189\n    .end local v5    # \"e\":Ljava/security/cert/CertificateException;\n    .restart local v8    # \"pathStrings\":[Ljava/lang/String;\n    :cond_0\n    aget-object v4, v8, v10\n\n    .line 193\n    .local v4, \"currentPath\":Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    invoke-direct {v0, v4}, Lit/necst/grabnrun/SecureDexClassLoader;->getPackageNamesFromContainerPath(Ljava/lang/String;)Ljava/util/List;\n\n    move-result-object v7\n\n    .line 195\n    .local v7, \"packageNameList\":Ljava/util/List;, \"Ljava/util/List<Ljava/lang/String;>;\"\n    if-eqz v7, :cond_2\n\n    invoke-interface {v7}, Ljava/util/List;->isEmpty()Z\n\n    move-result v12\n\n    if-nez v12, :cond_2\n\n    .line 197\n    invoke-interface {v7}, Ljava/util/List;->iterator()Ljava/util/Iterator;\n\n    move-result-object v12\n\n    :cond_1\n    :goto_2\n    invoke-interface {v12}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v13\n\n    if-nez v13, :cond_3\n\n    .line 189\n    :cond_2\n    add-int/lit8 v10, v10, 0x1\n\n    goto :goto_1\n\n    .line 197\n    :cond_3\n    invoke-interface {v12}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v6\n\n    check-cast v6, Ljava/lang/String;\n\n    .line 200\n    .local v6, \"packageName\":Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v13, v6, v4}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v9\n\n    check-cast v9, Ljava/lang/String;\n\n    .line 203\n    .local v9, \"previousPath\":Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-virtual {v13, v6}, Lit/necst/grabnrun/PackageNameTrie;->generateEntriesForPackageName(Ljava/lang/String;)V\n\n    .line 207\n    if-eqz v9, :cond_1\n\n    .line 211\n    sget-object v13, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"Package Name \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v14, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    const-string v15, \" is not unique!\\n Previous path: \"\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    .line 212\n    invoke-virtual {v14, v9}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    const-string v15, \";\\n New path: \"\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    const-string v15, \";\"\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    .line 211\n    invoke-static {v13, v14}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_2\n.end method\n\n.method static synthetic access$0(Lit/necst/grabnrun/SecureDexClassLoader;Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n    .locals 1\n\n    .prologue\n    .line 882\n    invoke-direct {p0, p1}, Lit/necst/grabnrun/SecureDexClassLoader;->importCertificateFromPackageName(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n\n.method static synthetic access$1(Lit/necst/grabnrun/SecureDexClassLoader;Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n    .locals 1\n\n    .prologue\n    .line 919\n    invoke-direct {p0, p1, p2}, Lit/necst/grabnrun/SecureDexClassLoader;->verifyContainerSignatureAgainstCertificate(Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n\n    move-result v0\n\n    return v0\n.end method\n\n.method private downloadCertificateRemotelyViaHttps(Ljava/lang/String;)Z\n    .locals 4\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 1202\n    iget-object v2, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v2, p1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Ljava/net/URL;\n\n    .line 1206\n    .local v0, \"certificateRemoteURL\":Ljava/net/URL;\n    new-instance v2, Ljava/lang/StringBuilder;\n\n    iget-object v3, p0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFolder:Ljava/io/File;\n\n    invoke-virtual {v3}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-static {v3}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v3\n\n    invoke-direct {v2, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v3, \"/\"\n\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    invoke-virtual {v2, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    const-string v3, \".pem\"\n\n    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v2\n\n    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v1\n\n    .line 1209\n    .local v1, \"localCertPath\":Ljava/lang/String;\n    iget-object v2, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n    const/4 v3, 0x0\n\n    invoke-virtual {v2, v0, v1, v3}, Lit/necst/grabnrun/FileDownloader;->downloadRemoteUrl(Ljava/net/URL;Ljava/lang/String;Z)Z\n\n    move-result v2\n\n    return v2\n.end method\n\n.method private getPackageNamesFromContainerPath(Ljava/lang/String;)Ljava/util/List;\n    .locals 20\n    .param p1, \"containerPath\"    # Ljava/lang/String;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \")\",\n            \"Ljava/util/List\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;\"\n        }\n    .end annotation\n\n    .prologue\n    .line 222\n    if-eqz p1, :cond_0\n\n    invoke-virtual/range {p1 .. p1}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v18\n\n    if-nez v18, :cond_0\n\n    new-instance v18, Ljava/io/File;\n\n    move-object/from16 v0, v18\n\n    move-object/from16 v1, p1\n\n    invoke-direct {v0, v1}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual/range {v18 .. v18}, Ljava/io/File;->exists()Z\n\n    move-result v18\n\n    if-nez v18, :cond_2\n\n    :cond_0\n    const/4 v15, 0x0\n\n    .line 342\n    :cond_1\n    :goto_0\n    return-object v15\n\n    .line 225\n    :cond_2\n    const-string v18, \".\"\n\n    move-object/from16 v0, p1\n\n    move-object/from16 v1, v18\n\n    invoke-virtual {v0, v1}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v9\n\n    .line 227\n    .local v9, \"extensionIndex\":I\n    const/16 v18, -0x1\n\n    move/from16 v0, v18\n\n    if-ne v9, v0, :cond_3\n\n    const/4 v15, 0x0\n\n    goto :goto_0\n\n    .line 229\n    :cond_3\n    move-object/from16 v0, p1\n\n    invoke-virtual {v0, v9}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v8\n\n    .line 231\n    .local v8, \"extension\":Ljava/lang/String;\n    new-instance v15, Ljava/util/ArrayList;\n\n    invoke-direct {v15}, Ljava/util/ArrayList;-><init>()V\n\n    .line 233\n    .local v15, \"packageNameList\":Ljava/util/List;, \"Ljava/util/List<Ljava/lang/String;>;\"\n    const-string v18, \".apk\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v8, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v18\n\n    if-eqz v18, :cond_5\n\n    .line 237\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageManager:Landroid/content/pm/PackageManager;\n\n    move-object/from16 v18, v0\n\n    const/16 v19, 0x0\n\n    move-object/from16 v0, v18\n\n    move-object/from16 v1, p1\n\n    move/from16 v2, v19\n\n    invoke-virtual {v0, v1, v2}, Landroid/content/pm/PackageManager;->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;\n\n    move-result-object v18\n\n    if-eqz v18, :cond_4\n\n    .line 239\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageManager:Landroid/content/pm/PackageManager;\n\n    move-object/from16 v18, v0\n\n    const/16 v19, 0x0\n\n    move-object/from16 v0, v18\n\n    move-object/from16 v1, p1\n\n    move/from16 v2, v19\n\n    invoke-virtual {v0, v1, v2}, Landroid/content/pm/PackageManager;->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;\n\n    move-result-object v18\n\n    move-object/from16 v0, v18\n\n    iget-object v0, v0, Landroid/content/pm/PackageInfo;->packageName:Ljava/lang/String;\n\n    move-object/from16 v18, v0\n\n    move-object/from16 v0, v18\n\n    invoke-interface {v15, v0}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n\n    goto :goto_0\n\n    .line 243\n    :cond_4\n    const/4 v15, 0x0\n\n    goto :goto_0\n\n    .line 246\n    :cond_5\n    const-string v18, \".jar\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v8, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v18\n\n    if-eqz v18, :cond_e\n\n    .line 253\n    const/4 v11, 0x0\n\n    .line 254\n    .local v11, \"isAValidJar\":Z\n    const/4 v3, 0x0\n\n    .line 259\n    .local v3, \"containerJar\":Ljava/util/jar/JarFile;\n    :try_start_0\n    new-instance v4, Ljava/util/jar/JarFile;\n\n    move-object/from16 v0, p1\n\n    invoke-direct {v4, v0}, Ljava/util/jar/JarFile;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 262\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .local v4, \"containerJar\":Ljava/util/jar/JarFile;\n    :try_start_1\n    const-string v18, \"classes.dex\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v4, v0}, Ljava/util/jar/JarFile;->getJarEntry(Ljava/lang/String;)Ljava/util/jar/JarEntry;\n    :try_end_1\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_5\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    move-result-object v18\n\n    if-eqz v18, :cond_6\n\n    .line 263\n    const/4 v11, 0x1\n\n    .line 268\n    :cond_6\n    if-eqz v4, :cond_7\n\n    .line 270\n    :try_start_2\n    invoke-virtual {v4}, Ljava/util/jar/JarFile;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_3\n\n    .line 276\n    :cond_7\n    :goto_1\n    if-eqz v11, :cond_d\n\n    .line 279\n    const/4 v6, 0x0\n\n    .line 284\n    .local v6, \"dexFile\":Ldalvik/system/DexFile;\n    new-instance v16, Ljava/util/HashSet;\n\n    invoke-direct/range {v16 .. v16}, Ljava/util/HashSet;-><init>()V\n\n    .line 289\n    .local v16, \"packageNameSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :try_start_3\n    new-instance v18, Ljava/lang/StringBuilder;\n\n    const/16 v19, 0x0\n\n    move-object/from16 v0, p1\n\n    move/from16 v1, v19\n\n    invoke-virtual {v0, v1, v9}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v19\n\n    invoke-static/range {v19 .. v19}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v19\n\n    invoke-direct/range {v18 .. v19}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v19, \".odex\"\n\n    invoke-virtual/range {v18 .. v19}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v18\n\n    invoke-virtual/range {v18 .. v18}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v13\n\n    .line 292\n    .local v13, \"outputDexTempPath\":Ljava/lang/String;\n    const/16 v18, 0x0\n\n    move-object/from16 v0, p1\n\n    move/from16 v1, v18\n\n    invoke-static {v0, v13, v1}, Ldalvik/system/DexFile;->loadDex(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;\n\n    move-result-object v6\n\n    .line 294\n    invoke-virtual {v6}, Ldalvik/system/DexFile;->entries()Ljava/util/Enumeration;\n\n    move-result-object v5\n\n    .line 296\n    .local v5, \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    :cond_8\n    :goto_2\n    invoke-interface {v5}, Ljava/util/Enumeration;->hasMoreElements()Z\n\n    move-result v18\n\n    if-nez v18, :cond_b\n\n    .line 318\n    new-instance v18, Ljava/io/File;\n\n    move-object/from16 v0, v18\n\n    invoke-direct {v0, v13}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual/range {v18 .. v18}, Ljava/io/File;->delete()Z\n    :try_end_3\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_4\n\n    .line 328\n    invoke-interface/range {v16 .. v16}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v17\n\n    .line 330\n    .local v17, \"packageNameSetIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :goto_3\n    invoke-interface/range {v17 .. v17}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v18\n\n    if-eqz v18, :cond_1\n\n    .line 331\n    invoke-interface/range {v17 .. v17}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v18\n\n    check-cast v18, Ljava/lang/String;\n\n    move-object/from16 v0, v18\n\n    invoke-interface {v15, v0}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n\n    goto :goto_3\n\n    .line 265\n    .end local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    .end local v5    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .end local v6    # \"dexFile\":Ldalvik/system/DexFile;\n    .end local v13    # \"outputDexTempPath\":Ljava/lang/String;\n    .end local v16    # \"packageNameSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    .end local v17    # \"packageNameSetIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .restart local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    :catch_0\n    move-exception v7\n\n    .line 268\n    .local v7, \"e\":Ljava/io/IOException;\n    :goto_4\n    if-eqz v3, :cond_9\n\n    .line 270\n    :try_start_4\n    invoke-virtual {v3}, Ljava/util/jar/JarFile;->close()V\n    :try_end_4\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_1\n\n    .line 266\n    :cond_9\n    :goto_5\n    const/4 v15, 0x0\n\n    goto/16 :goto_0\n\n    .line 271\n    :catch_1\n    move-exception v7\n\n    .line 272\n    invoke-virtual {v7}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_5\n\n    .line 267\n    .end local v7    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v18\n\n    .line 268\n    :goto_6\n    if-eqz v3, :cond_a\n\n    .line 270\n    :try_start_5\n    invoke-virtual {v3}, Ljava/util/jar/JarFile;->close()V\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_2\n\n    .line 274\n    :cond_a\n    :goto_7\n    throw v18\n\n    .line 271\n    :catch_2\n    move-exception v7\n\n    .line 272\n    .restart local v7    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v7}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_7\n\n    .line 271\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .end local v7    # \"e\":Ljava/io/IOException;\n    .restart local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    :catch_3\n    move-exception v7\n\n    .line 272\n    .restart local v7    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v7}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_1\n\n    .line 299\n    .end local v7    # \"e\":Ljava/io/IOException;\n    .restart local v5    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .restart local v6    # \"dexFile\":Ldalvik/system/DexFile;\n    .restart local v13    # \"outputDexTempPath\":Ljava/lang/String;\n    .restart local v16    # \"packageNameSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :cond_b\n    :try_start_6\n    invoke-interface {v5}, Ljava/util/Enumeration;->nextElement()Ljava/lang/Object;\n\n    move-result-object v10\n\n    check-cast v10, Ljava/lang/String;\n\n    .line 304\n    .local v10, \"fullClassName\":Ljava/lang/String;\n    :goto_8\n    const-string v18, \" \"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v10, v0}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v18\n\n    if-nez v18, :cond_c\n\n    .line 307\n    const-string v18, \".\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v10, v0}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v12\n\n    .line 309\n    .local v12, \"lastIndexPackageName\":I\n    const/16 v18, -0x1\n\n    move/from16 v0, v18\n\n    if-eq v12, v0, :cond_8\n\n    .line 311\n    const/16 v18, 0x0\n\n    move/from16 v0, v18\n\n    invoke-virtual {v10, v0, v12}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v14\n\n    .line 312\n    .local v14, \"packageName\":Ljava/lang/String;\n    move-object/from16 v0, v16\n\n    invoke-interface {v0, v14}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n\n    goto :goto_2\n\n    .line 320\n    .end local v5    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .end local v10    # \"fullClassName\":Ljava/lang/String;\n    .end local v12    # \"lastIndexPackageName\":I\n    .end local v13    # \"outputDexTempPath\":Ljava/lang/String;\n    .end local v14    # \"packageName\":Ljava/lang/String;\n    :catch_4\n    move-exception v7\n\n    .line 322\n    .restart local v7    # \"e\":Ljava/io/IOException;\n    const/4 v15, 0x0\n\n    goto/16 :goto_0\n\n    .line 305\n    .end local v7    # \"e\":Ljava/io/IOException;\n    .restart local v5    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .restart local v10    # \"fullClassName\":Ljava/lang/String;\n    .restart local v13    # \"outputDexTempPath\":Ljava/lang/String;\n    :cond_c\n    const/16 v18, 0x1\n\n    invoke-virtual {v10}, Ljava/lang/String;->length()I\n\n    move-result v19\n\n    move/from16 v0, v18\n\n    move/from16 v1, v19\n\n    invoke-virtual {v10, v0, v1}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_4\n\n    move-result-object v10\n\n    goto :goto_8\n\n    .line 337\n    .end local v5    # \"dexEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/lang/String;>;\"\n    .end local v6    # \"dexFile\":Ldalvik/system/DexFile;\n    .end local v10    # \"fullClassName\":Ljava/lang/String;\n    .end local v13    # \"outputDexTempPath\":Ljava/lang/String;\n    .end local v16    # \"packageNameSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :cond_d\n    const/4 v15, 0x0\n\n    goto/16 :goto_0\n\n    .line 342\n    .end local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    .end local v11    # \"isAValidJar\":Z\n    :cond_e\n    const/4 v15, 0x0\n\n    goto/16 :goto_0\n\n    .line 267\n    .restart local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v11    # \"isAValidJar\":Z\n    :catchall_1\n    move-exception v18\n\n    move-object v3, v4\n\n    .end local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    goto :goto_6\n\n    .line 265\n    .end local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    :catch_5\n    move-exception v7\n\n    move-object v3, v4\n\n    .end local v4    # \"containerJar\":Ljava/util/jar/JarFile;\n    .restart local v3    # \"containerJar\":Ljava/util/jar/JarFile;\n    goto :goto_4\n.end method\n\n.method private importCertificateFromAppPrivateDir(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n    .locals 13\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    const/4 v12, 0x0\n\n    .line 1118\n    iget-object v10, p0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFolder:Ljava/io/File;\n\n    new-instance v11, Lit/necst/grabnrun/CertFileFilter;\n\n    invoke-direct {v11, p1}, Lit/necst/grabnrun/CertFileFilter;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v10, v11}, Ljava/io/File;->listFiles(Ljava/io/FileFilter;)[Ljava/io/File;\n\n    move-result-object v4\n\n    .line 1120\n    .local v4, \"certMatchingFiles\":[Ljava/io/File;\n    const/4 v9, 0x0\n\n    .line 1122\n    .local v9, \"verifiedCertificate\":Ljava/security/cert/X509Certificate;\n    if-eqz v4, :cond_1\n\n    array-length v10, v4\n\n    if-eqz v10, :cond_1\n\n    .line 1125\n    const/4 v6, 0x0\n\n    .line 1132\n    .local v6, \"inStream\":Ljava/io/InputStream;\n    :try_start_0\n    new-instance v7, Ljava/io/FileInputStream;\n\n    const/4 v10, 0x0\n\n    aget-object v10, v4, v10\n\n    invoke-direct {v7, v10}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V\n    :try_end_0\n    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_1\n    .catch Ljava/security/cert/CertificateException; {:try_start_0 .. :try_end_0} :catch_3\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 1134\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    .local v7, \"inStream\":Ljava/io/InputStream;\n    :try_start_1\n    iget-object v10, p0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFactory:Ljava/security/cert/CertificateFactory;\n\n    invoke-virtual {v10, v7}, Ljava/security/cert/CertificateFactory;->generateCertificate(Ljava/io/InputStream;)Ljava/security/cert/Certificate;\n\n    move-result-object v10\n\n    move-object v0, v10\n\n    check-cast v0, Ljava/security/cert/X509Certificate;\n\n    move-object v9, v0\n    :try_end_1\n    .catch Ljava/io/FileNotFoundException; {:try_start_1 .. :try_end_1} :catch_9\n    .catch Ljava/security/cert/CertificateException; {:try_start_1 .. :try_end_1} :catch_8\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    .line 1141\n    if-eqz v7, :cond_3\n\n    .line 1143\n    :try_start_2\n    invoke-virtual {v7}, Ljava/io/InputStream;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_6\n\n    move-object v6, v7\n\n    .line 1152\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v6    # \"inStream\":Ljava/io/InputStream;\n    :cond_0\n    :goto_0\n    if-eqz v9, :cond_1\n\n    .line 1155\n    :try_start_3\n    invoke-virtual {v9}, Ljava/security/cert/X509Certificate;->checkValidity()V\n\n    .line 1159\n    invoke-virtual {v9}, Ljava/security/cert/X509Certificate;->getKeyUsage()[Z\n\n    move-result-object v10\n\n    if-eqz v10, :cond_5\n\n    .line 1161\n    const/4 v8, 0x5\n\n    .line 1162\n    .local v8, \"keyCertSignIndex\":I\n    invoke-virtual {v9}, Ljava/security/cert/X509Certificate;->getKeyUsage()[Z\n\n    move-result-object v10\n\n    aget-boolean v10, v10, v8\n\n    if-eqz v10, :cond_4\n\n    .line 1163\n    new-instance v10, Ljava/security/cert/CertificateExpiredException;\n\n    const-string v11, \"This certificate should not be used for code verification!\"\n\n    invoke-direct {v10, v11}, Ljava/security/cert/CertificateExpiredException;-><init>(Ljava/lang/String;)V\n\n    throw v10\n    :try_end_3\n    .catch Ljava/security/cert/CertificateExpiredException; {:try_start_3 .. :try_end_3} :catch_0\n    .catch Ljava/security/cert/CertificateNotYetValidException; {:try_start_3 .. :try_end_3} :catch_7\n\n    .line 1178\n    .end local v8    # \"keyCertSignIndex\":I\n    :catch_0\n    move-exception v5\n\n    .line 1182\n    .local v5, \"e\":Ljava/security/cert/CertificateException;\n    :goto_1\n    const/4 v9, 0x0\n\n    .line 1183\n    aget-object v10, v4, v12\n\n    invoke-virtual {v10}, Ljava/io/File;->getName()Ljava/lang/String;\n\n    move-result-object v3\n\n    .line 1184\n    .local v3, \"certFileToErase\":Ljava/lang/String;\n    aget-object v10, v4, v12\n\n    invoke-virtual {v10}, Ljava/io/File;->delete()Z\n\n    move-result v10\n\n    if-eqz v10, :cond_7\n\n    .line 1185\n    sget-object v10, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v11, Ljava/lang/StringBuilder;\n\n    const-string v12, \"Expired certificate \"\n\n    invoke-direct {v11, v12}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v11, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v11\n\n    const-string v12, \" has been erased.\"\n\n    invoke-virtual {v11, v12}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v11\n\n    invoke-virtual {v11}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-static {v10, v11}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1195\n    .end local v3    # \"certFileToErase\":Ljava/lang/String;\n    .end local v5    # \"e\":Ljava/security/cert/CertificateException;\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    :cond_1\n    :goto_2\n    return-object v9\n\n    .line 1136\n    .restart local v6    # \"inStream\":Ljava/io/InputStream;\n    :catch_1\n    move-exception v5\n\n    .line 1137\n    .local v5, \"e\":Ljava/io/FileNotFoundException;\n    :goto_3\n    :try_start_4\n    invoke-virtual {v5}, Ljava/io/FileNotFoundException;->printStackTrace()V\n    :try_end_4\n    .catchall {:try_start_4 .. :try_end_4} :catchall_0\n\n    .line 1141\n    if-eqz v6, :cond_0\n\n    .line 1143\n    :try_start_5\n    invoke-virtual {v6}, Ljava/io/InputStream;->close()V\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_2\n\n    goto :goto_0\n\n    .line 1144\n    :catch_2\n    move-exception v5\n\n    .line 1145\n    .local v5, \"e\":Ljava/io/IOException;\n    invoke-virtual {v5}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_0\n\n    .line 1138\n    .end local v5    # \"e\":Ljava/io/IOException;\n    :catch_3\n    move-exception v5\n\n    .line 1139\n    .local v5, \"e\":Ljava/security/cert/CertificateException;\n    :goto_4\n    :try_start_6\n    invoke-virtual {v5}, Ljava/security/cert/CertificateException;->printStackTrace()V\n    :try_end_6\n    .catchall {:try_start_6 .. :try_end_6} :catchall_0\n\n    .line 1141\n    if-eqz v6, :cond_0\n\n    .line 1143\n    :try_start_7\n    invoke-virtual {v6}, Ljava/io/InputStream;->close()V\n    :try_end_7\n    .catch Ljava/io/IOException; {:try_start_7 .. :try_end_7} :catch_4\n\n    goto :goto_0\n\n    .line 1144\n    :catch_4\n    move-exception v5\n\n    .line 1145\n    .local v5, \"e\":Ljava/io/IOException;\n    invoke-virtual {v5}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_0\n\n    .line 1140\n    .end local v5    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v10\n\n    .line 1141\n    :goto_5\n    if-eqz v6, :cond_2\n\n    .line 1143\n    :try_start_8\n    invoke-virtual {v6}, Ljava/io/InputStream;->close()V\n    :try_end_8\n    .catch Ljava/io/IOException; {:try_start_8 .. :try_end_8} :catch_5\n\n    .line 1148\n    :cond_2\n    :goto_6\n    throw v10\n\n    .line 1144\n    :catch_5\n    move-exception v5\n\n    .line 1145\n    .restart local v5    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v5}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_6\n\n    .line 1144\n    .end local v5    # \"e\":Ljava/io/IOException;\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    :catch_6\n    move-exception v5\n\n    .line 1145\n    .restart local v5    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v5}, Ljava/io/IOException;->printStackTrace()V\n\n    .end local v5    # \"e\":Ljava/io/IOException;\n    :cond_3\n    move-object v6, v7\n\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v6    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_0\n\n    .line 1165\n    .restart local v8    # \"keyCertSignIndex\":I\n    :cond_4\n    :try_start_9\n    sget-object v10, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    invoke-virtual {v9}, Ljava/security/cert/X509Certificate;->getKeyUsage()[Z\n\n    move-result-object v11\n\n    invoke-virtual {v11}, Ljava/lang/Object;->toString()Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-static {v10, v11}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1171\n    .end local v8    # \"keyCertSignIndex\":I\n    :cond_5\n    const-string v1, \"C=US,O=Android,CN=Android Debug\"\n\n    .line 1172\n    .local v1, \"androidDebugModeDN\":Ljava/lang/String;\n    new-instance v2, Ljavax/security/auth/x500/X500Principal;\n\n    invoke-direct {v2, v1}, Ljavax/security/auth/x500/X500Principal;-><init>(Ljava/lang/String;)V\n\n    .line 1173\n    .local v2, \"androidDebugModePrincipal\":Ljavax/security/auth/x500/X500Principal;\n    invoke-virtual {v9}, Ljava/security/cert/X509Certificate;->getIssuerX500Principal()Ljavax/security/auth/x500/X500Principal;\n\n    move-result-object v10\n\n    invoke-virtual {v10, v2}, Ljavax/security/auth/x500/X500Principal;->equals(Ljava/lang/Object;)Z\n\n    move-result v10\n\n    if-nez v10, :cond_6\n\n    .line 1174\n    invoke-virtual {v9}, Ljava/security/cert/X509Certificate;->getSubjectX500Principal()Ljavax/security/auth/x500/X500Principal;\n\n    move-result-object v10\n\n    invoke-virtual {v10, v2}, Ljavax/security/auth/x500/X500Principal;->equals(Ljava/lang/Object;)Z\n\n    move-result v10\n\n    if-eqz v10, :cond_1\n\n    .line 1175\n    :cond_6\n    new-instance v10, Ljava/security/cert/CertificateExpiredException;\n\n    const-string v11, \"Android Debug Certificate can\\'t be accepted to sign containers!\"\n\n    invoke-direct {v10, v11}, Ljava/security/cert/CertificateExpiredException;-><init>(Ljava/lang/String;)V\n\n    throw v10\n    :try_end_9\n    .catch Ljava/security/cert/CertificateExpiredException; {:try_start_9 .. :try_end_9} :catch_0\n    .catch Ljava/security/cert/CertificateNotYetValidException; {:try_start_9 .. :try_end_9} :catch_7\n\n    .line 1178\n    .end local v1    # \"androidDebugModeDN\":Ljava/lang/String;\n    .end local v2    # \"androidDebugModePrincipal\":Ljavax/security/auth/x500/X500Principal;\n    :catch_7\n    move-exception v5\n\n    goto/16 :goto_1\n\n    .line 1187\n    .restart local v3    # \"certFileToErase\":Ljava/lang/String;\n    .local v5, \"e\":Ljava/security/cert/CertificateException;\n    :cond_7\n    sget-object v10, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v11, Ljava/lang/StringBuilder;\n\n    const-string v12, \"Problems while deleting expired certificate \"\n\n    invoke-direct {v11, v12}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v11, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v11\n\n    const-string v12, \"!\"\n\n    invoke-virtual {v11, v12}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v11\n\n    invoke-virtual {v11}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-static {v10, v11}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_2\n\n    .line 1140\n    .end local v3    # \"certFileToErase\":Ljava/lang/String;\n    .end local v5    # \"e\":Ljava/security/cert/CertificateException;\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    :catchall_1\n    move-exception v10\n\n    move-object v6, v7\n\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v6    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_5\n\n    .line 1138\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    :catch_8\n    move-exception v5\n\n    move-object v6, v7\n\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v6    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_4\n\n    .line 1136\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    :catch_9\n    move-exception v5\n\n    move-object v6, v7\n\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v6    # \"inStream\":Ljava/io/InputStream;\n    goto/16 :goto_3\n.end method\n\n.method private importCertificateFromPackageName(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n    .locals 2\n    .param p1, \"packageName\"    # Ljava/lang/String;\n\n    .prologue\n    .line 889\n    invoke-direct {p0, p1}, Lit/necst/grabnrun/SecureDexClassLoader;->importCertificateFromAppPrivateDir(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n\n    move-result-object v1\n\n    .line 891\n    .local v1, \"verifiedCertificate\":Ljava/security/cert/X509Certificate;\n    if-nez v1, :cond_0\n\n    .line 898\n    invoke-direct {p0, p1}, Lit/necst/grabnrun/SecureDexClassLoader;->downloadCertificateRemotelyViaHttps(Ljava/lang/String;)Z\n\n    move-result v0\n\n    .line 902\n    .local v0, \"isCertificateDownloadSuccessful\":Z\n    if-eqz v0, :cond_0\n\n    .line 907\n    invoke-direct {p0, p1}, Lit/necst/grabnrun/SecureDexClassLoader;->importCertificateFromAppPrivateDir(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n\n    move-result-object v1\n\n    .line 914\n    .end local v0    # \"isCertificateDownloadSuccessful\":Z\n    :cond_0\n    return-object v1\n.end method\n\n.method private revertPackageNameToURL(Ljava/lang/String;)Ljava/net/URL;\n    .locals 11\n    .param p1, \"packageName\"    # Ljava/lang/String;\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/net/MalformedURLException;\n        }\n    .end annotation\n\n    .prologue\n    const/4 v5, -0x1\n\n    const/16 v10, 0x2e\n\n    .line 424\n    invoke-virtual {p1, v10}, Ljava/lang/String;->indexOf(I)I\n\n    move-result v1\n\n    .line 426\n    .local v1, \"firstPointChar\":I\n    if-ne v1, v5, :cond_0\n\n    .line 429\n    new-instance v4, Ljava/net/URL;\n\n    const-string v5, \"https\"\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-static {p1}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v7, \".com\"\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    const-string v7, \"certificate.pem\"\n\n    invoke-direct {v4, v5, v6, v7}, Ljava/net/URL;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V\n\n    .line 446\n    :goto_0\n    return-object v4\n\n    .line 433\n    :cond_0\n    const/4 v4, 0x0\n\n    invoke-virtual {p1, v4, v1}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v0\n\n    .line 434\n    .local v0, \"firstLevelDomain\":Ljava/lang/String;\n    add-int/lit8 v4, v1, 0x1\n\n    invoke-virtual {p1, v10, v4}, Ljava/lang/String;->indexOf(II)I\n\n    move-result v3\n\n    .line 436\n    .local v3, \"secondPointChar\":I\n    if-ne v3, v5, :cond_1\n\n    .line 438\n    new-instance v4, Ljava/net/URL;\n\n    const-string v5, \"https\"\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    add-int/lit8 v7, v1, 0x1\n\n    invoke-virtual {p1, v7}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v7, \".\"\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    const-string v7, \"/certificate.pem\"\n\n    invoke-direct {v4, v5, v6, v7}, Ljava/net/URL;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V\n\n    goto :goto_0\n\n    .line 444\n    :cond_1\n    add-int/lit8 v4, v1, 0x1\n\n    invoke-virtual {p1, v4, v3}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v2\n\n    .line 446\n    .local v2, \"secondLevelDomain\":Ljava/lang/String;\n    new-instance v4, Ljava/net/URL;\n\n    const-string v5, \"https\"\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-static {v2}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v7, \".\"\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    add-int/lit8 v8, v3, 0x1\n\n    invoke-virtual {p1, v8}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v8\n\n    sget-char v9, Ljava/io/File;->separatorChar:C\n\n    invoke-virtual {v8, v10, v9}, Ljava/lang/String;->replace(CC)Ljava/lang/String;\n\n    move-result-object v8\n\n    invoke-static {v8}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v8\n\n    invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v8, \"/certificate.pem\"\n\n    invoke-virtual {v7, v8}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v4, v5, v6, v7}, Ljava/net/URL;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V\n\n    goto :goto_0\n.end method\n\n.method private verifyAllContainersSignature()V\n    .locals 11\n\n    .prologue\n    .line 462\n    new-instance v0, Ljava/util/HashMap;\n\n    invoke-direct {v0}, Ljava/util/HashMap;-><init>()V\n\n    .line 465\n    .local v0, \"alreadyCheckedContainerMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/lang/Boolean;>;\"\n    iget-object v8, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v8}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v8\n\n    invoke-interface {v8}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v4\n\n    .line 467\n    .local v4, \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_0\n    :goto_0\n    invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v8\n\n    if-nez v8, :cond_1\n\n    .line 536\n    return-void\n\n    .line 469\n    :cond_1\n    invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v3\n\n    check-cast v3, Ljava/lang/String;\n\n    .line 470\n    .local v3, \"currentPackageName\":Ljava/lang/String;\n    iget-object v8, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v8, v3}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/String;\n\n    .line 473\n    .local v1, \"containerPath\":Ljava/lang/String;\n    invoke-interface {v0, v1}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v8\n\n    if-eqz v8, :cond_2\n\n    .line 477\n    invoke-interface {v0, v1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v8\n\n    check-cast v8, Ljava/lang/Boolean;\n\n    invoke-virtual {v8}, Ljava/lang/Boolean;->booleanValue()Z\n\n    move-result v8\n\n    if-nez v8, :cond_0\n\n    .line 478\n    invoke-interface {v4}, Ljava/util/Iterator;->remove()V\n\n    goto :goto_0\n\n    .line 487\n    :cond_2\n    iget-object v8, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-virtual {v8, v3}, Lit/necst/grabnrun/PackageNameTrie;->getPackageNameWithAssociatedCertificate(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 489\n    .local v5, \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    const/4 v7, 0x0\n\n    .line 492\n    .local v7, \"verifiedCertificate\":Ljava/security/cert/X509Certificate;\n    invoke-virtual {v5}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v8\n\n    if-nez v8, :cond_3\n\n    .line 495\n    invoke-direct {p0, v5}, Lit/necst/grabnrun/SecureDexClassLoader;->importCertificateFromPackageName(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n\n    move-result-object v7\n\n    .line 499\n    :cond_3\n    const/4 v6, 0x1\n\n    .line 501\n    .local v6, \"signatureCheckIsSuccessful\":Z\n    if-eqz v7, :cond_4\n\n    .line 507\n    invoke-direct {p0, v1, v7}, Lit/necst/grabnrun/SecureDexClassLoader;->verifyContainerSignatureAgainstCertificate(Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n\n    move-result v6\n\n    .line 510\n    if-eqz v6, :cond_4\n\n    .line 514\n    const/4 v8, 0x1\n\n    invoke-static {v8}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;\n\n    move-result-object v8\n\n    invoke-interface {v0, v1, v8}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 518\n    :cond_4\n    if-eqz v7, :cond_5\n\n    if-eqz v7, :cond_0\n\n    if-nez v6, :cond_0\n\n    .line 522\n    :cond_5\n    const/4 v8, 0x0\n\n    invoke-static {v8}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;\n\n    move-result-object v8\n\n    invoke-interface {v0, v1, v8}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 525\n    new-instance v2, Ljava/io/File;\n\n    invoke-direct {v2, v1}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 526\n    .local v2, \"containerToRemove\":Ljava/io/File;\n    invoke-virtual {v2}, Ljava/io/File;->delete()Z\n\n    move-result v8\n\n    if-nez v8, :cond_6\n\n    .line 527\n    sget-object v8, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v9, Ljava/lang/StringBuilder;\n\n    const-string v10, \"It was impossible to delete \"\n\n    invoke-direct {v9, v10}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v9, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v9\n\n    invoke-virtual {v9}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v9\n\n    invoke-static {v8, v9}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 531\n    :cond_6\n    invoke-interface {v4}, Ljava/util/Iterator;->remove()V\n\n    goto :goto_0\n.end method\n\n.method private verifyAllContainersSignatureConcurrently(Ljava/util/Set;)V\n    .locals 22\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/Set\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \">;)V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 546\n    .local p1, \"containersPathToVerifySet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    new-instance v5, Ljava/util/LinkedHashMap;\n\n    invoke-direct {v5}, Ljava/util/LinkedHashMap;-><init>()V\n\n    .line 549\n    .local v5, \"containerPathToRootPackageNameMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;\"\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    move-object/from16 v18, v0\n\n    invoke-interface/range {v18 .. v18}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v18\n\n    invoke-interface/range {v18 .. v18}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v13\n\n    .line 553\n    .local v13, \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_0\n    :goto_0\n    invoke-interface {v13}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v18\n\n    if-nez v18, :cond_4\n\n    .line 569\n    new-instance v18, Ljava/util/HashSet;\n\n    invoke-direct/range {v18 .. v18}, Ljava/util/HashSet;-><init>()V\n\n    invoke-static/range {v18 .. v18}, Ljava/util/Collections;->synchronizedSet(Ljava/util/Set;)Ljava/util/Set;\n\n    move-result-object v16\n\n    .line 571\n    .local v16, \"successVerifiedContainerPathSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    invoke-interface {v5}, Ljava/util/Map;->isEmpty()Z\n\n    move-result v18\n\n    if-nez v18, :cond_1\n\n    .line 575\n    invoke-interface {v5}, Ljava/util/Map;->size()I\n\n    move-result v18\n\n    invoke-static/range {v18 .. v18}, Ljava/util/concurrent/Executors;->newFixedThreadPool(I)Ljava/util/concurrent/ExecutorService;\n\n    move-result-object v17\n\n    .line 576\n    .local v17, \"threadSignatureVerificationPool\":Ljava/util/concurrent/ExecutorService;\n    new-instance v11, Ljava/util/ArrayList;\n\n    invoke-direct {v11}, Ljava/util/ArrayList;-><init>()V\n\n    .line 578\n    .local v11, \"futureTaskList\":Ljava/util/List;, \"Ljava/util/List<Ljava/util/concurrent/Future<*>;>;\"\n    invoke-interface {v5}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v18\n\n    invoke-interface/range {v18 .. v18}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v4\n\n    .line 580\n    .local v4, \"containerPathIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :goto_1\n    invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v18\n\n    if-nez v18, :cond_5\n\n    .line 591\n    invoke-interface/range {v17 .. v17}, Ljava/util/concurrent/ExecutorService;->shutdown()V\n\n    .line 593\n    invoke-interface {v11}, Ljava/util/List;->iterator()Ljava/util/Iterator;\n\n    move-result-object v18\n\n    :goto_2\n    invoke-interface/range {v18 .. v18}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v19\n\n    if-nez v19, :cond_6\n\n    .line 610\n    const-wide/16 v18, 0x2\n\n    :try_start_0\n    sget-object v20, Lit/necst/grabnrun/SecureDexClassLoader;->KEEP_ALIVE_TIME_UNIT:Ljava/util/concurrent/TimeUnit;\n\n    invoke-interface/range {v17 .. v20}, Ljava/util/concurrent/ExecutorService;->awaitTermination(JLjava/util/concurrent/TimeUnit;)Z\n    :try_end_0\n    .catch Ljava/lang/InterruptedException; {:try_start_0 .. :try_end_0} :catch_1\n\n    .line 620\n    .end local v4    # \"containerPathIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .end local v11    # \"futureTaskList\":Ljava/util/List;, \"Ljava/util/List<Ljava/util/concurrent/Future<*>;>;\"\n    .end local v17    # \"threadSignatureVerificationPool\":Ljava/util/concurrent/ExecutorService;\n    :cond_1\n    :goto_3\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    move-object/from16 v18, v0\n\n    invoke-interface/range {v18 .. v18}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v18\n\n    invoke-interface/range {v18 .. v18}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v12\n\n    .line 622\n    .local v12, \"packageNamesAfterVerificationIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_2\n    :goto_4\n    invoke-interface {v12}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v18\n\n    if-nez v18, :cond_7\n\n    .line 640\n    invoke-interface/range {p1 .. p1}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v6\n\n    .line 642\n    .local v6, \"containersPathToVerifyIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_3\n    :goto_5\n    invoke-interface {v6}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v18\n\n    if-nez v18, :cond_9\n\n    .line 654\n    return-void\n\n    .line 555\n    .end local v6    # \"containersPathToVerifyIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .end local v12    # \"packageNamesAfterVerificationIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .end local v16    # \"successVerifiedContainerPathSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :cond_4\n    invoke-interface {v13}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v8\n\n    check-cast v8, Ljava/lang/String;\n\n    .line 559\n    .local v8, \"currentPackageName\":Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    move-object/from16 v18, v0\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v0, v8}, Lit/necst/grabnrun/PackageNameTrie;->getPackageNameWithAssociatedCertificate(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v15\n\n    .line 561\n    .local v15, \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    invoke-virtual {v15}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v18\n\n    if-nez v18, :cond_0\n\n    .line 564\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    move-object/from16 v18, v0\n\n    move-object/from16 v0, v18\n\n    invoke-interface {v0, v8}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v18\n\n    check-cast v18, Ljava/lang/String;\n\n    move-object/from16 v0, v18\n\n    invoke-interface {v5, v0, v15}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    goto/16 :goto_0\n\n    .line 582\n    .end local v8    # \"currentPackageName\":Ljava/lang/String;\n    .end local v15    # \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    .restart local v4    # \"containerPathIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .restart local v11    # \"futureTaskList\":Ljava/util/List;, \"Ljava/util/List<Ljava/util/concurrent/Future<*>;>;\"\n    .restart local v16    # \"successVerifiedContainerPathSet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    .restart local v17    # \"threadSignatureVerificationPool\":Ljava/util/concurrent/ExecutorService;\n    :cond_5\n    invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v7\n\n    check-cast v7, Ljava/lang/String;\n\n    .line 586\n    .local v7, \"currentContainerPath\":Ljava/lang/String;\n    new-instance v19, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;\n\n    invoke-interface {v5, v7}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v18\n\n    check-cast v18, Ljava/lang/String;\n\n    move-object/from16 v0, v19\n\n    move-object/from16 v1, p0\n\n    move-object/from16 v2, v18\n\n    move-object/from16 v3, v16\n\n    invoke-direct {v0, v1, v7, v2, v3}, Lit/necst/grabnrun/SecureDexClassLoader$SignatureVerificationTask;-><init>(Lit/necst/grabnrun/SecureDexClassLoader;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V\n\n    move-object/from16 v0, v17\n\n    move-object/from16 v1, v19\n\n    invoke-interface {v0, v1}, Ljava/util/concurrent/ExecutorService;->submit(Ljava/lang/Runnable;)Ljava/util/concurrent/Future;\n\n    move-result-object v10\n\n    .line 587\n    .local v10, \"futureTask\":Ljava/util/concurrent/Future;, \"Ljava/util/concurrent/Future<*>;\"\n    invoke-interface {v11, v10}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n\n    goto/16 :goto_1\n\n    .line 593\n    .end local v7    # \"currentContainerPath\":Ljava/lang/String;\n    .end local v10    # \"futureTask\":Ljava/util/concurrent/Future;, \"Ljava/util/concurrent/Future<*>;\"\n    :cond_6\n    invoke-interface/range {v18 .. v18}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v10\n\n    check-cast v10, Ljava/util/concurrent/Future;\n\n    .line 598\n    .restart local v10    # \"futureTask\":Ljava/util/concurrent/Future;, \"Ljava/util/concurrent/Future<*>;\"\n    :try_start_1\n    invoke-interface {v10}, Ljava/util/concurrent/Future;->get()Ljava/lang/Object;\n    :try_end_1\n    .catch Ljava/lang/InterruptedException; {:try_start_1 .. :try_end_1} :catch_0\n    .catch Ljava/util/concurrent/ExecutionException; {:try_start_1 .. :try_end_1} :catch_2\n\n    goto/16 :goto_2\n\n    .line 600\n    :catch_0\n    move-exception v9\n\n    .line 603\n    .local v9, \"e\":Ljava/lang/Exception;\n    :goto_6\n    sget-object v19, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v20, Ljava/lang/StringBuilder;\n\n    const-string v21, \"One of the thread failed during signature verification because of \"\n\n    invoke-direct/range {v20 .. v21}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v9}, Ljava/lang/Exception;->getCause()Ljava/lang/Throwable;\n\n    move-result-object v21\n\n    invoke-virtual/range {v21 .. v21}, Ljava/lang/Throwable;->toString()Ljava/lang/String;\n\n    move-result-object v21\n\n    invoke-virtual/range {v20 .. v21}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v20\n\n    invoke-virtual/range {v20 .. v20}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v20\n\n    invoke-static/range {v19 .. v20}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_2\n\n    .line 611\n    .end local v9    # \"e\":Ljava/lang/Exception;\n    .end local v10    # \"futureTask\":Ljava/util/concurrent/Future;, \"Ljava/util/concurrent/Future<*>;\"\n    :catch_1\n    move-exception v9\n\n    .line 614\n    .local v9, \"e\":Ljava/lang/InterruptedException;\n    sget-object v18, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    const-string v19, \"At least one thread for signature verification was still busy and so it was interrupted\"\n\n    invoke-static/range {v18 .. v19}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_3\n\n    .line 624\n    .end local v4    # \"containerPathIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .end local v9    # \"e\":Ljava/lang/InterruptedException;\n    .end local v11    # \"futureTaskList\":Ljava/util/List;, \"Ljava/util/List<Ljava/util/concurrent/Future<*>;>;\"\n    .end local v17    # \"threadSignatureVerificationPool\":Ljava/util/concurrent/ExecutorService;\n    .restart local v12    # \"packageNamesAfterVerificationIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_7\n    invoke-interface {v12}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v8\n\n    check-cast v8, Ljava/lang/String;\n\n    .line 628\n    .restart local v8    # \"currentPackageName\":Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    move-object/from16 v18, v0\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v0, v8}, Lit/necst/grabnrun/PackageNameTrie;->getPackageNameWithAssociatedCertificate(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v14\n\n    .line 630\n    .local v14, \"rootPackageNameAllowedForLoading\":Ljava/lang/String;\n    invoke-virtual {v14}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v18\n\n    if-nez v18, :cond_8\n\n    move-object/from16 v0, p0\n\n    iget-object v0, v0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    move-object/from16 v18, v0\n\n    move-object/from16 v0, v18\n\n    invoke-interface {v0, v8}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v18\n\n    move-object/from16 v0, v16\n\n    move-object/from16 v1, v18\n\n    invoke-interface {v0, v1}, Ljava/util/Set;->contains(Ljava/lang/Object;)Z\n\n    move-result v18\n\n    if-nez v18, :cond_2\n\n    .line 634\n    :cond_8\n    invoke-interface {v12}, Ljava/util/Iterator;->remove()V\n\n    goto/16 :goto_4\n\n    .line 644\n    .end local v8    # \"currentPackageName\":Ljava/lang/String;\n    .end local v14    # \"rootPackageNameAllowedForLoading\":Ljava/lang/String;\n    .restart local v6    # \"containersPathToVerifyIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_9\n    invoke-interface {v6}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v7\n\n    check-cast v7, Ljava/lang/String;\n\n    .line 646\n    .restart local v7    # \"currentContainerPath\":Ljava/lang/String;\n    move-object/from16 v0, v16\n\n    invoke-interface {v0, v7}, Ljava/util/Set;->contains(Ljava/lang/Object;)Z\n\n    move-result v18\n\n    if-nez v18, :cond_3\n\n    .line 650\n    new-instance v18, Ljava/io/File;\n\n    move-object/from16 v0, v18\n\n    invoke-direct {v0, v7}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual/range {v18 .. v18}, Ljava/io/File;->delete()Z\n\n    move-result v18\n\n    if-nez v18, :cond_3\n\n    .line 651\n    sget-object v18, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v19, Ljava/lang/StringBuilder;\n\n    const-string v20, \"Issue while deleting conainer located at \"\n\n    invoke-direct/range {v19 .. v20}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v19\n\n    invoke-virtual {v0, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v19\n\n    invoke-virtual/range {v19 .. v19}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v19\n\n    invoke-static/range {v18 .. v19}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_5\n\n    .line 600\n    .end local v6    # \"containersPathToVerifyIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .end local v7    # \"currentContainerPath\":Ljava/lang/String;\n    .end local v12    # \"packageNamesAfterVerificationIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .restart local v4    # \"containerPathIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    .restart local v10    # \"futureTask\":Ljava/util/concurrent/Future;, \"Ljava/util/concurrent/Future<*>;\"\n    .restart local v11    # \"futureTaskList\":Ljava/util/List;, \"Ljava/util/List<Ljava/util/concurrent/Future<*>;>;\"\n    .restart local v17    # \"threadSignatureVerificationPool\":Ljava/util/concurrent/ExecutorService;\n    :catch_2\n    move-exception v9\n\n    goto/16 :goto_6\n.end method\n\n.method private verifyContainerSignatureAgainstCertificate(Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n    .locals 15\n    .param p1, \"containerPath\"    # Ljava/lang/String;\n    .param p2, \"verifiedCertificate\"    # Ljava/security/cert/X509Certificate;\n\n    .prologue\n    .line 926\n    const-string v12, \".\"\n\n    move-object/from16 v0, p1\n\n    invoke-virtual {v0, v12}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v6\n\n    .line 927\n    .local v6, \"extensionIndex\":I\n    move-object/from16 v0, p1\n\n    invoke-virtual {v0, v6}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 929\n    .local v5, \"extension\":Ljava/lang/String;\n    const/4 v10, 0x0\n\n    .line 933\n    .local v10, \"signatureCheckIsSuccessful\":Z\n    const-string v12, \".apk\"\n\n    invoke-virtual {v5, v12}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v12\n\n    if-eqz v12, :cond_0\n\n    .line 940\n    iget-object v12, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageManager:Landroid/content/pm/PackageManager;\n\n    const/16 v13, 0x40\n\n    move-object/from16 v0, p1\n\n    invoke-virtual {v12, v0, v13}, Landroid/content/pm/PackageManager;->getPackageArchiveInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;\n\n    move-result-object v12\n\n    iget-object v11, v12, Landroid/content/pm/PackageInfo;->signatures:[Landroid/content/pm/Signature;\n\n    .line 942\n    .local v11, \"signatures\":[Landroid/content/pm/Signature;\n    if-eqz v11, :cond_0\n\n    .line 943\n    array-length v14, v11\n\n    const/4 v12, 0x0\n\n    move v13, v12\n\n    :goto_0\n    if-lt v13, v14, :cond_3\n\n    .line 984\n    .end local v11    # \"signatures\":[Landroid/content/pm/Signature;\n    :cond_0\n    const-string v12, \".jar\"\n\n    invoke-virtual {v5, v12}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v12\n\n    if-nez v12, :cond_1\n\n    const-string v12, \".apk\"\n\n    invoke-virtual {v5, v12}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v12\n\n    if-eqz v12, :cond_2\n\n    if-eqz v10, :cond_2\n\n    .line 987\n    :cond_1\n    const/4 v2, 0x0\n\n    .line 991\n    .local v2, \"containerToVerify\":Ljava/util/jar/JarFile;\n    :try_start_0\n    new-instance v3, Ljava/util/jar/JarFile;\n\n    move-object/from16 v0, p1\n\n    invoke-direct {v3, v0}, Ljava/util/jar/JarFile;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_4\n    .catchall {:try_start_0 .. :try_end_0} :catchall_1\n\n    .line 995\n    .end local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    .local v3, \"containerToVerify\":Ljava/util/jar/JarFile;\n    :try_start_1\n    move-object/from16 v0, p2\n\n    invoke-direct {p0, v3, v0}, Lit/necst/grabnrun/SecureDexClassLoader;->verifyJARContainer(Ljava/util/jar/JarFile;Ljava/security/cert/X509Certificate;)V\n    :try_end_1\n    .catch Ljava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_8\n    .catchall {:try_start_1 .. :try_end_1} :catchall_2\n\n    .line 999\n    const/4 v10, 0x1\n\n    .line 1007\n    if-eqz v3, :cond_2\n\n    .line 1009\n    :try_start_2\n    invoke-virtual {v3}, Ljava/util/jar/JarFile;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_7\n\n    .line 1021\n    .end local v3    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    :cond_2\n    :goto_1\n    return v10\n\n    .line 943\n    .restart local v11    # \"signatures\":[Landroid/content/pm/Signature;\n    :cond_3\n    aget-object v9, v11, v13\n\n    .line 944\n    .local v9, \"sign\":Landroid/content/pm/Signature;\n    if-eqz v9, :cond_5\n\n    .line 946\n    const/4 v1, 0x0\n\n    .line 947\n    .local v1, \"certFromSign\":Ljava/security/cert/X509Certificate;\n    const/4 v7, 0x0\n\n    .line 952\n    .local v7, \"inStream\":Ljava/io/InputStream;\n    :try_start_3\n    new-instance v8, Ljava/io/ByteArrayInputStream;\n\n    invoke-virtual {v9}, Landroid/content/pm/Signature;->toByteArray()[B\n\n    move-result-object v12\n\n    invoke-direct {v8, v12}, Ljava/io/ByteArrayInputStream;-><init>([B)V\n    :try_end_3\n    .catch Ljava/security/cert/CertificateException; {:try_start_3 .. :try_end_3} :catch_0\n    .catchall {:try_start_3 .. :try_end_3} :catchall_0\n\n    .line 953\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .local v8, \"inStream\":Ljava/io/InputStream;\n    :try_start_4\n    iget-object v12, p0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFactory:Ljava/security/cert/CertificateFactory;\n\n    invoke-virtual {v12, v8}, Ljava/security/cert/CertificateFactory;->generateCertificate(Ljava/io/InputStream;)Ljava/security/cert/Certificate;\n\n    move-result-object v12\n\n    move-object v0, v12\n\n    check-cast v0, Ljava/security/cert/X509Certificate;\n\n    move-object v1, v0\n\n    .line 956\n    invoke-virtual {v1}, Ljava/security/cert/X509Certificate;->checkValidity()V\n\n    .line 960\n    move-object/from16 v0, p2\n\n    invoke-virtual {v1, v0}, Ljava/security/cert/X509Certificate;->equals(Ljava/lang/Object;)Z\n    :try_end_4\n    .catch Ljava/security/cert/CertificateException; {:try_start_4 .. :try_end_4} :catch_9\n    .catchall {:try_start_4 .. :try_end_4} :catchall_3\n\n    move-result v12\n\n    if-eqz v12, :cond_4\n\n    .line 963\n    const/4 v10, 0x1\n\n    .line 968\n    :cond_4\n    if-eqz v8, :cond_5\n\n    .line 970\n    :try_start_5\n    invoke-virtual {v8}, Ljava/io/InputStream;->close()V\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_3\n\n    .line 943\n    .end local v1    # \"certFromSign\":Ljava/security/cert/X509Certificate;\n    .end local v8    # \"inStream\":Ljava/io/InputStream;\n    :cond_5\n    :goto_2\n    add-int/lit8 v12, v13, 0x1\n\n    move v13, v12\n\n    goto :goto_0\n\n    .line 965\n    .restart local v1    # \"certFromSign\":Ljava/security/cert/X509Certificate;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    :catch_0\n    move-exception v12\n\n    .line 968\n    :goto_3\n    if-eqz v7, :cond_5\n\n    .line 970\n    :try_start_6\n    invoke-virtual {v7}, Ljava/io/InputStream;->close()V\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_1\n\n    goto :goto_2\n\n    .line 971\n    :catch_1\n    move-exception v4\n\n    .line 972\n    .local v4, \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_2\n\n    .line 967\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v12\n\n    .line 968\n    :goto_4\n    if-eqz v7, :cond_6\n\n    .line 970\n    :try_start_7\n    invoke-virtual {v7}, Ljava/io/InputStream;->close()V\n    :try_end_7\n    .catch Ljava/io/IOException; {:try_start_7 .. :try_end_7} :catch_2\n\n    .line 975\n    :cond_6\n    :goto_5\n    throw v12\n\n    .line 971\n    :catch_2\n    move-exception v4\n\n    .line 972\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_5\n\n    .line 971\n    .end local v4    # \"e\":Ljava/io/IOException;\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v8    # \"inStream\":Ljava/io/InputStream;\n    :catch_3\n    move-exception v4\n\n    .line 972\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_2\n\n    .line 1001\n    .end local v1    # \"certFromSign\":Ljava/security/cert/X509Certificate;\n    .end local v4    # \"e\":Ljava/io/IOException;\n    .end local v8    # \"inStream\":Ljava/io/InputStream;\n    .end local v9    # \"sign\":Landroid/content/pm/Signature;\n    .end local v11    # \"signatures\":[Landroid/content/pm/Signature;\n    .restart local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    :catch_4\n    move-exception v4\n\n    .line 1004\n    .local v4, \"e\":Ljava/lang/Exception;\n    :goto_6\n    const/4 v10, 0x0\n\n    .line 1007\n    if-eqz v2, :cond_2\n\n    .line 1009\n    :try_start_8\n    invoke-virtual {v2}, Ljava/util/jar/JarFile;->close()V\n    :try_end_8\n    .catch Ljava/io/IOException; {:try_start_8 .. :try_end_8} :catch_5\n\n    goto :goto_1\n\n    .line 1010\n    :catch_5\n    move-exception v4\n\n    .line 1011\n    .local v4, \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_1\n\n    .line 1006\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catchall_1\n    move-exception v12\n\n    .line 1007\n    :goto_7\n    if-eqz v2, :cond_7\n\n    .line 1009\n    :try_start_9\n    invoke-virtual {v2}, Ljava/util/jar/JarFile;->close()V\n    :try_end_9\n    .catch Ljava/io/IOException; {:try_start_9 .. :try_end_9} :catch_6\n\n    .line 1014\n    :cond_7\n    :goto_8\n    throw v12\n\n    .line 1010\n    :catch_6\n    move-exception v4\n\n    .line 1011\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_8\n\n    .line 1010\n    .end local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    .end local v4    # \"e\":Ljava/io/IOException;\n    .restart local v3    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    :catch_7\n    move-exception v4\n\n    .line 1011\n    .restart local v4    # \"e\":Ljava/io/IOException;\n    invoke-virtual {v4}, Ljava/io/IOException;->printStackTrace()V\n\n    goto :goto_1\n\n    .line 1006\n    .end local v4    # \"e\":Ljava/io/IOException;\n    :catchall_2\n    move-exception v12\n\n    move-object v2, v3\n\n    .end local v3    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    .restart local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    goto :goto_7\n\n    .line 1001\n    .end local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    .restart local v3    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    :catch_8\n    move-exception v4\n\n    move-object v2, v3\n\n    .end local v3    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    .restart local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    goto :goto_6\n\n    .line 967\n    .end local v2    # \"containerToVerify\":Ljava/util/jar/JarFile;\n    .restart local v1    # \"certFromSign\":Ljava/security/cert/X509Certificate;\n    .restart local v8    # \"inStream\":Ljava/io/InputStream;\n    .restart local v9    # \"sign\":Landroid/content/pm/Signature;\n    .restart local v11    # \"signatures\":[Landroid/content/pm/Signature;\n    :catchall_3\n    move-exception v12\n\n    move-object v7, v8\n\n    .end local v8    # \"inStream\":Ljava/io/InputStream;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_4\n\n    .line 965\n    .end local v7    # \"inStream\":Ljava/io/InputStream;\n    .restart local v8    # \"inStream\":Ljava/io/InputStream;\n    :catch_9\n    move-exception v12\n\n    move-object v7, v8\n\n    .end local v8    # \"inStream\":Ljava/io/InputStream;\n    .restart local v7    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_3\n.end method\n\n.method private verifyJARContainer(Ljava/util/jar/JarFile;Ljava/security/cert/X509Certificate;)V\n    .locals 16\n    .param p1, \"jarFile\"    # Ljava/util/jar/JarFile;\n    .param p2, \"trustedCert\"    # Ljava/security/cert/X509Certificate;\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/io/IOException;\n        }\n    .end annotation\n\n    .prologue\n    .line 1027\n    if-eqz p1, :cond_0\n\n    if-nez p2, :cond_1\n\n    .line 1028\n    :cond_0\n    new-instance v13, Ljava/lang/SecurityException;\n\n    const-string v14, \"JarFile or certificate are missing\"\n\n    invoke-direct {v13, v14}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V\n\n    throw v13\n\n    .line 1030\n    :cond_1\n    new-instance v5, Ljava/util/Vector;\n\n    invoke-direct {v5}, Ljava/util/Vector;-><init>()V\n\n    .line 1033\n    .local v5, \"entriesVec\":Ljava/util/Vector;, \"Ljava/util/Vector<Ljava/util/jar/JarEntry;>;\"\n    invoke-virtual/range {p1 .. p1}, Ljava/util/jar/JarFile;->getManifest()Ljava/util/jar/Manifest;\n\n    move-result-object v8\n\n    .line 1034\n    .local v8, \"man\":Ljava/util/jar/Manifest;\n    if-nez v8, :cond_2\n\n    .line 1035\n    sget-object v13, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    invoke-virtual/range {p1 .. p1}, Ljava/util/jar/JarFile;->getName()Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-static {v15}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v15, \"is not signed.\"\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1036\n    new-instance v13, Ljava/lang/SecurityException;\n\n    const-string v14, \"The container is not signed\"\n\n    invoke-direct {v13, v14}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V\n\n    throw v13\n\n    .line 1040\n    :cond_2\n    const/16 v13, 0x2000\n\n    new-array v1, v13, [B\n\n    .line 1041\n    .local v1, \"buffer\":[B\n    invoke-virtual/range {p1 .. p1}, Ljava/util/jar/JarFile;->entries()Ljava/util/Enumeration;\n\n    move-result-object v4\n\n    .line 1043\n    .local v4, \"entries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;\"\n    :cond_3\n    :goto_0\n    invoke-interface {v4}, Ljava/util/Enumeration;->hasMoreElements()Z\n\n    move-result v13\n\n    if-nez v13, :cond_5\n\n    .line 1065\n    invoke-virtual {v5}, Ljava/util/Vector;->elements()Ljava/util/Enumeration;\n\n    move-result-object v10\n\n    .line 1067\n    .local v10, \"signedEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;\"\n    :cond_4\n    invoke-interface {v10}, Ljava/util/Enumeration;->hasMoreElements()Z\n\n    move-result v13\n\n    if-nez v13, :cond_7\n\n    .line 1112\n    return-void\n\n    .line 1046\n    .end local v10    # \"signedEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;\"\n    :cond_5\n    invoke-interface {v4}, Ljava/util/Enumeration;->nextElement()Ljava/lang/Object;\n\n    move-result-object v7\n\n    check-cast v7, Ljava/util/jar/JarEntry;\n\n    .line 1049\n    .local v7, \"je\":Ljava/util/jar/JarEntry;\n    invoke-virtual {v7}, Ljava/util/jar/JarEntry;->isDirectory()Z\n\n    move-result v13\n\n    if-nez v13, :cond_3\n\n    .line 1050\n    invoke-virtual {v5, v7}, Ljava/util/Vector;->addElement(Ljava/lang/Object;)V\n\n    .line 1051\n    move-object/from16 v0, p1\n\n    invoke-virtual {v0, v7}, Ljava/util/jar/JarFile;->getInputStream(Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;\n\n    move-result-object v6\n\n    .line 1055\n    .local v6, \"inStream\":Ljava/io/InputStream;\n    :cond_6\n    const/4 v13, 0x0\n\n    array-length v14, v1\n\n    invoke-virtual {v6, v1, v13, v14}, Ljava/io/InputStream;->read([BII)I\n\n    move-result v13\n\n    const/4 v14, -0x1\n\n    if-ne v13, v14, :cond_6\n\n    .line 1060\n    invoke-virtual {v6}, Ljava/io/InputStream;->close()V\n\n    goto :goto_0\n\n    .line 1069\n    .end local v6    # \"inStream\":Ljava/io/InputStream;\n    .end local v7    # \"je\":Ljava/util/jar/JarEntry;\n    .restart local v10    # \"signedEntries\":Ljava/util/Enumeration;, \"Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;\"\n    :cond_7\n    invoke-interface {v10}, Ljava/util/Enumeration;->nextElement()Ljava/lang/Object;\n\n    move-result-object v11\n\n    check-cast v11, Ljava/util/jar/JarEntry;\n\n    .line 1072\n    .local v11, \"signedEntry\":Ljava/util/jar/JarEntry;\n    invoke-virtual {v11}, Ljava/util/jar/JarEntry;->getCertificates()[Ljava/security/cert/Certificate;\n\n    move-result-object v2\n\n    .line 1073\n    .local v2, \"certificates\":[Ljava/security/cert/Certificate;\n    if-eqz v2, :cond_8\n\n    array-length v13, v2\n\n    if-nez v13, :cond_9\n\n    .line 1074\n    :cond_8\n    invoke-virtual {v11}, Ljava/util/jar/JarEntry;->getName()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"META-INF\"\n\n    invoke-virtual {v13, v14}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_4\n\n    .line 1075\n    sget-object v13, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    invoke-virtual {v11}, Ljava/util/jar/JarEntry;->getName()Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-static {v15}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v15, \" is an unsigned class file\"\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1076\n    new-instance v13, Ljava/lang/SecurityException;\n\n    const-string v14, \"The container has unsigned class files.\"\n\n    invoke-direct {v13, v14}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V\n\n    throw v13\n\n    .line 1083\n    :cond_9\n    const/4 v9, 0x0\n\n    .line 1085\n    .local v9, \"signedAsExpected\":Z\n    array-length v15, v2\n\n    const/4 v13, 0x0\n\n    move v14, v13\n\n    :goto_1\n    if-lt v14, v15, :cond_a\n\n    .line 1106\n    if-nez v9, :cond_4\n\n    .line 1107\n    sget-object v13, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"The trusted certificate was not used to sign \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v11}, Ljava/util/jar/JarEntry;->getName()Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1108\n    new-instance v13, Ljava/lang/SecurityException;\n\n    const-string v14, \"The provider is not signed by a trusted signer\"\n\n    invoke-direct {v13, v14}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V\n\n    throw v13\n\n    .line 1085\n    :cond_a\n    aget-object v12, v2, v14\n\n    .line 1089\n    .local v12, \"signerCert\":Ljava/security/cert/Certificate;\n    :try_start_0\n    move-object v0, v12\n\n    check-cast v0, Ljava/security/cert/X509Certificate;\n\n    move-object v13, v0\n\n    invoke-virtual {v13}, Ljava/security/cert/X509Certificate;->checkValidity()V\n    :try_end_0\n    .catch Ljava/security/cert/CertificateExpiredException; {:try_start_0 .. :try_end_0} :catch_0\n    .catch Ljava/security/cert/CertificateNotYetValidException; {:try_start_0 .. :try_end_0} :catch_2\n    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_1\n\n    .line 1101\n    :goto_2\n    move-object/from16 v0, p2\n\n    invoke-virtual {v12, v0}, Ljava/security/cert/Certificate;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_b\n\n    .line 1103\n    const/4 v9, 0x1\n\n    .line 1085\n    :cond_b\n    add-int/lit8 v13, v14, 0x1\n\n    move v14, v13\n\n    goto :goto_1\n\n    .line 1091\n    :catch_0\n    move-exception v3\n\n    .line 1095\n    .local v3, \"e\":Ljava/security/cert/CertificateException;\n    :goto_3\n    sget-object v13, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"One of the certificates used to sign \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v11}, Ljava/util/jar/JarEntry;->getName()Ljava/lang/String;\n\n    move-result-object v15\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    const-string v15, \" is expired\"\n\n    invoke-virtual {v14, v15}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 1096\n    new-instance v13, Ljava/lang/SecurityException;\n\n    const-string v14, \"One of the used certificates is expired!\"\n\n    invoke-direct {v13, v14}, Ljava/lang/SecurityException;-><init>(Ljava/lang/String;)V\n\n    throw v13\n\n    .line 1097\n    .end local v3    # \"e\":Ljava/security/cert/CertificateException;\n    :catch_1\n    move-exception v13\n\n    goto :goto_2\n\n    .line 1091\n    :catch_2\n    move-exception v3\n\n    goto :goto_3\n.end method\n\n\n# virtual methods\n.method public loadClass(Ljava/lang/String;)Ljava/lang/Class;\n    .locals 13\n    .param p1, \"className\"    # Ljava/lang/String;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \")\",\n            \"Ljava/lang/Class\",\n            \"<*>;\"\n        }\n    .end annotation\n\n    .annotation system Ldalvik/annotation/Throws;\n        value = {\n            Ljava/lang/ClassNotFoundException;\n        }\n    .end annotation\n\n    .prologue\n    const/4 v10, 0x0\n\n    .line 730\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v9}, Ljava/util/Map;->isEmpty()Z\n\n    move-result v9\n\n    if-eqz v9, :cond_0\n\n    move-object v9, v10\n\n    .line 875\n    :goto_0\n    return-object v9\n\n    .line 734\n    :cond_0\n    iget-boolean v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->hasBeenWipedOut:Z\n\n    if-eqz v9, :cond_1\n\n    move-object v9, v10\n\n    goto :goto_0\n\n    .line 739\n    :cond_1\n    const/4 v9, 0x0\n\n    const/16 v11, 0x2e\n\n    invoke-virtual {p1, v11}, Ljava/lang/String;->lastIndexOf(I)I\n\n    move-result v11\n\n    invoke-virtual {p1, v9, v11}, Ljava/lang/String;->substring(II)Ljava/lang/String;\n\n    move-result-object v4\n\n    .line 745\n    .local v4, \"packageName\":Ljava/lang/String;\n    iget-object v11, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    monitor-enter v11\n\n    .line 747\n    :try_start_0\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v9, v4}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/String;\n\n    .line 745\n    .local v1, \"containerPath\":Ljava/lang/String;\n    monitor-exit v11\n\n    .line 750\n    if-nez v1, :cond_2\n\n    move-object v9, v10\n\n    goto :goto_0\n\n    .line 745\n    .end local v1    # \"containerPath\":Ljava/lang/String;\n    :catchall_0\n    move-exception v9\n\n    monitor-exit v11\n    :try_end_0\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    throw v9\n\n    .line 752\n    .restart local v1    # \"containerPath\":Ljava/lang/String;\n    :cond_2\n    iget-boolean v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->performLazyEvaluation:Z\n\n    if-eqz v9, :cond_c\n\n    .line 759\n    iget-object v11, p0, Lit/necst/grabnrun/SecureDexClassLoader;->lazyAlreadyVerifiedPackageNameSet:Ljava/util/Set;\n\n    monitor-enter v11\n\n    .line 762\n    :try_start_1\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->lazyAlreadyVerifiedPackageNameSet:Ljava/util/Set;\n\n    invoke-interface {v9, v4}, Ljava/util/Set;->contains(Ljava/lang/Object;)Z\n\n    move-result v0\n\n    .line 759\n    .local v0, \"alreadyVerifiedPackageName\":Z\n    monitor-exit v11\n    :try_end_1\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    .line 765\n    if-eqz v0, :cond_3\n\n    .line 769\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-virtual {v9, v4}, Lit/necst/grabnrun/PackageNameTrie;->getPackageNameWithAssociatedCertificate(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v6\n\n    .line 771\n    .local v6, \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    invoke-virtual {v6}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v9\n\n    if-nez v9, :cond_c\n\n    .line 775\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mDexClassLoader:Ldalvik/system/DexClassLoader;\n\n    invoke-virtual {v9, p1}, Ldalvik/system/DexClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;\n\n    move-result-object v9\n\n    goto :goto_0\n\n    .line 759\n    .end local v0    # \"alreadyVerifiedPackageName\":Z\n    .end local v6    # \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    :catchall_1\n    move-exception v9\n\n    :try_start_2\n    monitor-exit v11\n    :try_end_2\n    .catchall {:try_start_2 .. :try_end_2} :catchall_1\n\n    throw v9\n\n    .line 784\n    .restart local v0    # \"alreadyVerifiedPackageName\":Z\n    :cond_3\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-virtual {v9, v4}, Lit/necst/grabnrun/PackageNameTrie;->getPackageNameWithAssociatedCertificate(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v6\n\n    .line 786\n    .restart local v6    # \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    const/4 v8, 0x0\n\n    .line 789\n    .local v8, \"verifiedCertificate\":Ljava/security/cert/X509Certificate;\n    invoke-virtual {v6}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v9\n\n    if-nez v9, :cond_4\n\n    .line 792\n    invoke-direct {p0, v6}, Lit/necst/grabnrun/SecureDexClassLoader;->importCertificateFromPackageName(Ljava/lang/String;)Ljava/security/cert/X509Certificate;\n\n    move-result-object v8\n\n    .line 795\n    :cond_4\n    if-eqz v8, :cond_b\n\n    .line 801\n    invoke-direct {p0, v1, v8}, Lit/necst/grabnrun/SecureDexClassLoader;->verifyContainerSignatureAgainstCertificate(Ljava/lang/String;Ljava/security/cert/X509Certificate;)Z\n\n    move-result v7\n\n    .line 804\n    .local v7, \"signatureCheckIsSuccessful\":Z\n    if-eqz v7, :cond_7\n\n    .line 819\n    iget-object v10, p0, Lit/necst/grabnrun/SecureDexClassLoader;->lazyAlreadyVerifiedPackageNameSet:Ljava/util/Set;\n\n    monitor-enter v10\n\n    .line 821\n    :try_start_3\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v9}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v9\n\n    invoke-interface {v9}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v5\n\n    .line 823\n    .local v5, \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_5\n    :goto_1\n    invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v9\n\n    if-nez v9, :cond_6\n\n    .line 819\n    monitor-exit v10\n    :try_end_3\n    .catchall {:try_start_3 .. :try_end_3} :catchall_2\n\n    .line 835\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mDexClassLoader:Ldalvik/system/DexClassLoader;\n\n    invoke-virtual {v9, p1}, Ldalvik/system/DexClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;\n\n    move-result-object v9\n\n    goto :goto_0\n\n    .line 825\n    :cond_6\n    :try_start_4\n    invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v3\n\n    check-cast v3, Ljava/lang/String;\n\n    .line 827\n    .local v3, \"currentPackageName\":Ljava/lang/String;\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v9, v3}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v9\n\n    check-cast v9, Ljava/lang/String;\n\n    invoke-virtual {v9, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v9\n\n    if-eqz v9, :cond_5\n\n    .line 830\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->lazyAlreadyVerifiedPackageNameSet:Ljava/util/Set;\n\n    invoke-interface {v9, v3}, Ljava/util/Set;->add(Ljava/lang/Object;)Z\n\n    goto :goto_1\n\n    .line 819\n    .end local v3    # \"currentPackageName\":Ljava/lang/String;\n    .end local v5    # \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :catchall_2\n    move-exception v9\n\n    monitor-exit v10\n    :try_end_4\n    .catchall {:try_start_4 .. :try_end_4} :catchall_2\n\n    throw v9\n\n    .line 840\n    :cond_7\n    new-instance v2, Ljava/io/File;\n\n    invoke-direct {v2, v1}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 841\n    .local v2, \"containerToRemove\":Ljava/io/File;\n    invoke-virtual {v2}, Ljava/io/File;->delete()Z\n\n    move-result v9\n\n    if-nez v9, :cond_8\n\n    .line 842\n    sget-object v9, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v11, Ljava/lang/StringBuilder;\n\n    const-string v12, \"It was impossible to delete \"\n\n    invoke-direct {v11, v12}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v11, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v11\n\n    invoke-virtual {v11}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-static {v9, v11}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 847\n    :cond_8\n    iget-object v11, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    monitor-enter v11\n\n    .line 849\n    :try_start_5\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v9}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v9\n\n    invoke-interface {v9}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v5\n\n    .line 851\n    .restart local v5    # \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_9\n    :goto_2\n    invoke-interface {v5}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v9\n\n    if-nez v9, :cond_a\n\n    .line 847\n    monitor-exit v11\n\n    move-object v9, v10\n\n    .line 860\n    goto/16 :goto_0\n\n    .line 853\n    :cond_a\n    invoke-interface {v5}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v3\n\n    check-cast v3, Ljava/lang/String;\n\n    .line 855\n    .restart local v3    # \"currentPackageName\":Ljava/lang/String;\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v9, v3}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v9\n\n    check-cast v9, Ljava/lang/String;\n\n    invoke-virtual {v9, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v9\n\n    if-eqz v9, :cond_9\n\n    .line 856\n    invoke-interface {v5}, Ljava/util/Iterator;->remove()V\n\n    goto :goto_2\n\n    .line 847\n    .end local v3    # \"currentPackageName\":Ljava/lang/String;\n    .end local v5    # \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :catchall_3\n    move-exception v9\n\n    monitor-exit v11\n    :try_end_5\n    .catchall {:try_start_5 .. :try_end_5} :catchall_3\n\n    throw v9\n\n    .end local v2    # \"containerToRemove\":Ljava/io/File;\n    .end local v7    # \"signatureCheckIsSuccessful\":Z\n    :cond_b\n    move-object v9, v10\n\n    .line 867\n    goto/16 :goto_0\n\n    .line 875\n    .end local v0    # \"alreadyVerifiedPackageName\":Z\n    .end local v6    # \"rootPackageNameWithCertificate\":Ljava/lang/String;\n    .end local v8    # \"verifiedCertificate\":Ljava/security/cert/X509Certificate;\n    :cond_c\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mDexClassLoader:Ldalvik/system/DexClassLoader;\n\n    invoke-virtual {v9, p1}, Ldalvik/system/DexClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;\n\n    move-result-object v9\n\n    goto/16 :goto_0\n.end method\n\n.method setCertificateLocationMap(Ljava/util/Map;)V\n    .locals 8\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;)V\"\n        }\n    .end annotation\n\n    .prologue\n    .line 348\n    .local p1, \"extPackageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    if-eqz p1, :cond_0\n\n    invoke-interface {p1}, Ljava/util/Map;->isEmpty()Z\n\n    move-result v5\n\n    if-nez v5, :cond_0\n\n    .line 349\n    iput-object p1, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    .line 354\n    :cond_0\n    iget-object v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v5}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v5\n\n    invoke-interface {v5}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v4\n\n    .line 356\n    .local v4, \"packageNameIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_1\n    :goto_0\n    invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v5\n\n    if-nez v5, :cond_3\n\n    .line 397\n    iget-boolean v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->performLazyEvaluation:Z\n\n    if-nez v5, :cond_2\n\n    .line 403\n    new-instance v1, Ljava/util/HashSet;\n\n    iget-object v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToContainerPathMap:Ljava/util/Map;\n\n    invoke-interface {v5}, Ljava/util/Map;->values()Ljava/util/Collection;\n\n    move-result-object v5\n\n    invoke-direct {v1, v5}, Ljava/util/HashSet;-><init>(Ljava/util/Collection;)V\n\n    .line 406\n    .local v1, \"containersToVerifySet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    invoke-interface {v1}, Ljava/util/Set;->size()I\n\n    move-result v5\n\n    const/4 v6, 0x2\n\n    if-ge v5, v6, :cond_5\n\n    .line 409\n    invoke-direct {p0}, Lit/necst/grabnrun/SecureDexClassLoader;->verifyAllContainersSignature()V\n\n    .line 417\n    .end local v1    # \"containersToVerifySet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :cond_2\n    :goto_1\n    return-void\n\n    .line 358\n    :cond_3\n    invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v2\n\n    check-cast v2, Ljava/lang/String;\n\n    .line 360\n    .local v2, \"currentPackageName\":Ljava/lang/String;\n    iget-object v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v5, v2}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v5\n\n    if-nez v5, :cond_4\n\n    .line 369\n    :try_start_0\n    invoke-direct {p0, v2}, Lit/necst/grabnrun/SecureDexClassLoader;->revertPackageNameToURL(Ljava/lang/String;)Ljava/net/URL;\n\n    move-result-object v0\n\n    .line 372\n    .local v0, \"certificateRemoteURL\":Ljava/net/URL;\n    iget-object v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v5, v2, v0}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n\n    .line 374\n    sget-object v5, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    const-string v7, \"Package Name: \"\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v6, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    .line 375\n    const-string v7, \"; Certificate Remote Location: \"\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    const-string v7, \";\"\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    .line 374\n    invoke-static {v5, v6}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_0\n    .catch Ljava/net/MalformedURLException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 388\n    .end local v0    # \"certificateRemoteURL\":Ljava/net/URL;\n    :cond_4\n    :goto_2\n    iget-object v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->packageNameToCertificateMap:Ljava/util/Map;\n\n    invoke-interface {v5, v2}, Ljava/util/Map;->containsKey(Ljava/lang/Object;)Z\n\n    move-result v5\n\n    if-eqz v5, :cond_1\n\n    .line 393\n    iget-object v5, p0, Lit/necst/grabnrun/SecureDexClassLoader;->mPackageNameTrie:Lit/necst/grabnrun/PackageNameTrie;\n\n    invoke-virtual {v5, v2}, Lit/necst/grabnrun/PackageNameTrie;->setEntryHasAssociatedCertificate(Ljava/lang/String;)V\n\n    goto :goto_0\n\n    .line 377\n    :catch_0\n    move-exception v3\n\n    .line 380\n    .local v3, \"e\":Ljava/net/MalformedURLException;\n    invoke-interface {v4}, Ljava/util/Iterator;->remove()V\n\n    .line 382\n    sget-object v5, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    const-string v7, \"It was impossible to revert package name \"\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    .line 383\n    invoke-virtual {v6, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    const-string v7, \" into a valid URL!\"\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    .line 382\n    invoke-static {v5, v6}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_2\n\n    .line 413\n    .end local v2    # \"currentPackageName\":Ljava/lang/String;\n    .end local v3    # \"e\":Ljava/net/MalformedURLException;\n    .restart local v1    # \"containersToVerifySet\":Ljava/util/Set;, \"Ljava/util/Set<Ljava/lang/String;>;\"\n    :cond_5\n    invoke-direct {p0, v1}, Lit/necst/grabnrun/SecureDexClassLoader;->verifyAllContainersSignatureConcurrently(Ljava/util/Set;)V\n\n    goto :goto_1\n.end method\n\n.method public wipeOutPrivateAppCachedData(ZZ)V\n    .locals 11\n    .param p1, \"containerPrivateFolder\"    # Z\n    .param p2, \"certificatePrivateFolder\"    # Z\n\n    .prologue\n    const/4 v8, 0x0\n\n    .line 1234\n    if-nez p1, :cond_0\n\n    if-nez p2, :cond_0\n\n    .line 1285\n    :goto_0\n    return-void\n\n    .line 1236\n    :cond_0\n    new-instance v7, Ljava/util/ArrayList;\n\n    invoke-direct {v7}, Ljava/util/ArrayList;-><init>()V\n\n    .line 1238\n    .local v7, \"fileToEraseList\":Ljava/util/List;, \"Ljava/util/List<Ljava/io/File;>;\"\n    if-eqz p1, :cond_1\n\n    .line 1242\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->resDownloadFolder:Ljava/io/File;\n\n    invoke-virtual {v9}, Ljava/io/File;->listFiles()[Ljava/io/File;\n\n    move-result-object v1\n\n    .line 1244\n    .local v1, \"containerFiles\":[Ljava/io/File;\n    array-length v10, v1\n\n    move v9, v8\n\n    :goto_1\n    if-lt v9, v10, :cond_4\n\n    .line 1250\n    .end local v1    # \"containerFiles\":[Ljava/io/File;\n    :cond_1\n    if-eqz p2, :cond_2\n\n    .line 1254\n    iget-object v9, p0, Lit/necst/grabnrun/SecureDexClassLoader;->certificateFolder:Ljava/io/File;\n\n    invoke-virtual {v9}, Ljava/io/File;->listFiles()[Ljava/io/File;\n\n    move-result-object v0\n\n    .line 1256\n    .local v0, \"certificateFiles\":[Ljava/io/File;\n    array-length v9, v0\n\n    :goto_2\n    if-lt v8, v9, :cond_5\n\n    .line 1262\n    .end local v0    # \"certificateFiles\":[Ljava/io/File;\n    :cond_2\n    invoke-interface {v7}, Ljava/util/List;->iterator()Ljava/util/Iterator;\n\n    move-result-object v6\n\n    .line 1264\n    .local v6, \"fileToEraseIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/io/File;>;\"\n    :cond_3\n    :goto_3\n    invoke-interface {v6}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v8\n\n    if-nez v8, :cond_6\n\n    .line 1283\n    const/4 v8, 0x1\n\n    iput-boolean v8, p0, Lit/necst/grabnrun/SecureDexClassLoader;->hasBeenWipedOut:Z\n\n    goto :goto_0\n\n    .line 1244\n    .end local v6    # \"fileToEraseIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/io/File;>;\"\n    .restart local v1    # \"containerFiles\":[Ljava/io/File;\n    :cond_4\n    aget-object v4, v1, v9\n\n    .line 1246\n    .local v4, \"file\":Ljava/io/File;\n    invoke-interface {v7, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n\n    .line 1244\n    add-int/lit8 v9, v9, 0x1\n\n    goto :goto_1\n\n    .line 1256\n    .end local v1    # \"containerFiles\":[Ljava/io/File;\n    .end local v4    # \"file\":Ljava/io/File;\n    .restart local v0    # \"certificateFiles\":[Ljava/io/File;\n    :cond_5\n    aget-object v4, v0, v8\n\n    .line 1258\n    .restart local v4    # \"file\":Ljava/io/File;\n    invoke-interface {v7, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z\n\n    .line 1256\n    add-int/lit8 v8, v8, 0x1\n\n    goto :goto_2\n\n    .line 1266\n    .end local v0    # \"certificateFiles\":[Ljava/io/File;\n    .end local v4    # \"file\":Ljava/io/File;\n    .restart local v6    # \"fileToEraseIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/io/File;>;\"\n    :cond_6\n    invoke-interface {v6}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v4\n\n    check-cast v4, Ljava/io/File;\n\n    .line 1270\n    .restart local v4    # \"file\":Ljava/io/File;\n    invoke-virtual {v4}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 1271\n    .local v5, \"filePath\":Ljava/lang/String;\n    const-string v8, \".\"\n\n    invoke-virtual {v5, v8}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v3\n\n    .line 1272\n    .local v3, \"extensionIndex\":I\n    invoke-virtual {v5, v3}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v2\n\n    .line 1274\n    .local v2, \"extension\":Ljava/lang/String;\n    const-string v8, \".apk\"\n\n    invoke-virtual {v2, v8}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v8\n\n    if-nez v8, :cond_7\n\n    const-string v8, \".jar\"\n\n    invoke-virtual {v2, v8}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v8\n\n    if-nez v8, :cond_7\n\n    const-string v8, \".pem\"\n\n    invoke-virtual {v2, v8}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v8\n\n    if-eqz v8, :cond_3\n\n    .line 1276\n    :cond_7\n    invoke-virtual {v4}, Ljava/io/File;->delete()Z\n\n    move-result v8\n\n    if-eqz v8, :cond_8\n\n    .line 1277\n    sget-object v8, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v9, Ljava/lang/StringBuilder;\n\n    invoke-static {v5}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v10\n\n    invoke-direct {v9, v10}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v10, \" has been erased.\"\n\n    invoke-virtual {v9, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v9\n\n    invoke-virtual {v9}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v9\n\n    invoke-static {v8, v9}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_3\n\n    .line 1279\n    :cond_8\n    sget-object v8, Lit/necst/grabnrun/SecureDexClassLoader;->TAG_SECURE_DEX_CLASS_LOADER:Ljava/lang/String;\n\n    new-instance v9, Ljava/lang/StringBuilder;\n\n    invoke-static {v5}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v10\n\n    invoke-direct {v9, v10}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const-string v10, \" was NOT erased.\"\n\n    invoke-virtual {v9, v10}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v9\n\n    invoke-virtual {v9}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v9\n\n    invoke-static {v8, v9}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_3\n.end method\n"
  },
  {
    "path": "repackPOC/smaliRes/grabnrun/SecureLoaderFactory.smali",
    "content": ".class public Lit/necst/grabnrun/SecureLoaderFactory;\n.super Ljava/lang/Object;\n.source \"SecureLoaderFactory.java\"\n\n\n# static fields\n.field static final CONT_IMPORT_DIR:Ljava/lang/String; = \"imported_cont\"\n\n.field public static final DEFAULT_DAYS_BEFORE_CONTAINER_EXPIRACY:I = 0x5\n\n.field private static final TAG_SECURE_FACTORY:Ljava/lang/String;\n\n\n# instance fields\n.field private daysBeforeContainerCacheExpiration:I\n\n.field private mContextWrapper:Landroid/content/ContextWrapper;\n\n.field private mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n.field private messageDigest:Ljava/security/MessageDigest;\n\n\n# direct methods\n.method static constructor <clinit>()V\n    .locals 1\n\n    .prologue\n    .line 49\n    const-class v0, Lit/necst/grabnrun/SecureLoaderFactory;\n\n    invoke-virtual {v0}, Ljava/lang/Class;->getSimpleName()Ljava/lang/String;\n\n    move-result-object v0\n\n    sput-object v0, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    .line 78\n    return-void\n.end method\n\n.method public constructor <init>(Landroid/content/ContextWrapper;)V\n    .locals 1\n    .param p1, \"parentContextWrapper\"    # Landroid/content/ContextWrapper;\n\n    .prologue\n    .line 95\n    const/4 v0, 0x5\n\n    invoke-direct {p0, p1, v0}, Lit/necst/grabnrun/SecureLoaderFactory;-><init>(Landroid/content/ContextWrapper;I)V\n\n    .line 96\n    return-void\n.end method\n\n.method public constructor <init>(Landroid/content/ContextWrapper;I)V\n    .locals 3\n    .param p1, \"parentContextWrapper\"    # Landroid/content/ContextWrapper;\n    .param p2, \"daysBeforeContainerCacheExpiration\"    # I\n\n    .prologue\n    .line 112\n    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n\n    .line 114\n    if-ltz p2, :cond_0\n\n    .line 117\n    iput p2, p0, Lit/necst/grabnrun/SecureLoaderFactory;->daysBeforeContainerCacheExpiration:I\n\n    .line 125\n    :goto_0\n    iput-object p1, p0, Lit/necst/grabnrun/SecureLoaderFactory;->mContextWrapper:Landroid/content/ContextWrapper;\n\n    .line 127\n    new-instance v1, Lit/necst/grabnrun/FileDownloader;\n\n    iget-object v2, p0, Lit/necst/grabnrun/SecureLoaderFactory;->mContextWrapper:Landroid/content/ContextWrapper;\n\n    invoke-direct {v1, v2}, Lit/necst/grabnrun/FileDownloader;-><init>(Landroid/content/ContextWrapper;)V\n\n    iput-object v1, p0, Lit/necst/grabnrun/SecureLoaderFactory;->mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n    .line 130\n    :try_start_0\n    const-string v1, \"SHA-1\"\n\n    invoke-static {v1}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;\n\n    move-result-object v1\n\n    iput-object v1, p0, Lit/necst/grabnrun/SecureLoaderFactory;->messageDigest:Ljava/security/MessageDigest;\n    :try_end_0\n    .catch Ljava/security/NoSuchAlgorithmException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 135\n    :goto_1\n    return-void\n\n    .line 122\n    :cond_0\n    const/4 v1, 0x5\n\n    iput v1, p0, Lit/necst/grabnrun/SecureLoaderFactory;->daysBeforeContainerCacheExpiration:I\n\n    goto :goto_0\n\n    .line 131\n    :catch_0\n    move-exception v0\n\n    .line 132\n    .local v0, \"e\":Ljava/security/NoSuchAlgorithmException;\n    sget-object v1, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v2, \"Wrong algorithm choice for message digest!\"\n\n    invoke-static {v1, v2}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 133\n    invoke-virtual {v0}, Ljava/security/NoSuchAlgorithmException;->printStackTrace()V\n\n    goto :goto_1\n.end method\n\n.method private computeDigestFromFilePath(Ljava/lang/String;)Ljava/lang/String;\n    .locals 10\n    .param p1, \"filePath\"    # Ljava/lang/String;\n\n    .prologue\n    .line 492\n    const/4 v4, 0x0\n\n    .line 493\n    .local v4, \"inStream\":Ljava/io/FileInputStream;\n    const/4 v2, 0x0\n\n    .line 497\n    .local v2, \"digestString\":Ljava/lang/String;\n    :try_start_0\n    new-instance v5, Ljava/io/FileInputStream;\n\n    invoke-direct {v5, p1}, Ljava/io/FileInputStream;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_7\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_2\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 499\n    .end local v4    # \"inStream\":Ljava/io/FileInputStream;\n    .local v5, \"inStream\":Ljava/io/FileInputStream;\n    const/16 v7, 0x2000\n\n    :try_start_1\n    new-array v0, v7, [B\n\n    .line 501\n    .local v0, \"buffer\":[B\n    :goto_0\n    invoke-virtual {v5, v0}, Ljava/io/FileInputStream;->read([B)I\n\n    move-result v6\n\n    .local v6, \"length\":I\n    const/4 v7, -0x1\n\n    if-ne v6, v7, :cond_1\n\n    .line 506\n    iget-object v7, p0, Lit/necst/grabnrun/SecureLoaderFactory;->messageDigest:Ljava/security/MessageDigest;\n\n    invoke-virtual {v7}, Ljava/security/MessageDigest;->digest()[B\n\n    move-result-object v1\n\n    .line 510\n    .local v1, \"digestBytes\":[B\n    const/16 v7, 0x8\n\n    invoke-static {v1, v7}, Landroid/util/Base64;->encodeToString([BI)Ljava/lang/String;\n\n    move-result-object v2\n\n    .line 511\n    const-string v7, \"line.separator\"\n\n    invoke-static {v7}, Ljava/lang/System;->getProperty(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v7\n\n    const-string v8, \"\"\n\n    invoke-virtual {v2, v7, v8}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;\n\n    move-result-object v7\n\n    const-string v8, \"\\r\"\n\n    const-string v9, \"\"\n\n    invoke-virtual {v7, v8, v9}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;\n    :try_end_1\n    .catch Ljava/io/FileNotFoundException; {:try_start_1 .. :try_end_1} :catch_0\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_6\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    move-result-object v2\n\n    .line 518\n    if-eqz v5, :cond_3\n\n    .line 520\n    :try_start_2\n    invoke-virtual {v5}, Ljava/io/FileInputStream;->close()V\n    :try_end_2\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_5\n\n    move-object v4, v5\n\n    .line 528\n    .end local v0    # \"buffer\":[B\n    .end local v1    # \"digestBytes\":[B\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .end local v6    # \"length\":I\n    .restart local v4    # \"inStream\":Ljava/io/FileInputStream;\n    :cond_0\n    :goto_1\n    return-object v2\n\n    .line 503\n    .end local v4    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v0    # \"buffer\":[B\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v6    # \"length\":I\n    :cond_1\n    :try_start_3\n    iget-object v7, p0, Lit/necst/grabnrun/SecureLoaderFactory;->messageDigest:Ljava/security/MessageDigest;\n\n    const/4 v8, 0x0\n\n    invoke-virtual {v7, v0, v8, v6}, Ljava/security/MessageDigest;->update([BII)V\n    :try_end_3\n    .catch Ljava/io/FileNotFoundException; {:try_start_3 .. :try_end_3} :catch_0\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_6\n    .catchall {:try_start_3 .. :try_end_3} :catchall_1\n\n    goto :goto_0\n\n    .line 513\n    .end local v0    # \"buffer\":[B\n    .end local v6    # \"length\":I\n    :catch_0\n    move-exception v3\n\n    move-object v4, v5\n\n    .line 514\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .local v3, \"e\":Ljava/io/FileNotFoundException;\n    .restart local v4    # \"inStream\":Ljava/io/FileInputStream;\n    :goto_2\n    :try_start_4\n    sget-object v7, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v8, Ljava/lang/StringBuilder;\n\n    const-string v9, \"No file found at \"\n\n    invoke-direct {v8, v9}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v8, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v8\n\n    invoke-virtual {v8}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v8\n\n    invoke-static {v7, v8}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_4\n    .catchall {:try_start_4 .. :try_end_4} :catchall_0\n\n    .line 518\n    if-eqz v4, :cond_0\n\n    .line 520\n    :try_start_5\n    invoke-virtual {v4}, Ljava/io/FileInputStream;->close()V\n    :try_end_5\n    .catch Ljava/io/IOException; {:try_start_5 .. :try_end_5} :catch_1\n\n    goto :goto_1\n\n    .line 521\n    :catch_1\n    move-exception v3\n\n    .line 522\n    .local v3, \"e\":Ljava/io/IOException;\n    sget-object v7, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v8, \"Issue while closing file stream in message digest computation!\"\n\n    invoke-static {v7, v8}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_1\n\n    .line 515\n    .end local v3    # \"e\":Ljava/io/IOException;\n    :catch_2\n    move-exception v3\n\n    .line 516\n    .restart local v3    # \"e\":Ljava/io/IOException;\n    :goto_3\n    :try_start_6\n    sget-object v7, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v8, \"Something went wrong while calculating the digest!\"\n\n    invoke-static {v7, v8}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_6\n    .catchall {:try_start_6 .. :try_end_6} :catchall_0\n\n    .line 518\n    if-eqz v4, :cond_0\n\n    .line 520\n    :try_start_7\n    invoke-virtual {v4}, Ljava/io/FileInputStream;->close()V\n    :try_end_7\n    .catch Ljava/io/IOException; {:try_start_7 .. :try_end_7} :catch_3\n\n    goto :goto_1\n\n    .line 521\n    :catch_3\n    move-exception v3\n\n    .line 522\n    sget-object v7, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v8, \"Issue while closing file stream in message digest computation!\"\n\n    invoke-static {v7, v8}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_1\n\n    .line 517\n    .end local v3    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v7\n\n    .line 518\n    :goto_4\n    if-eqz v4, :cond_2\n\n    .line 520\n    :try_start_8\n    invoke-virtual {v4}, Ljava/io/FileInputStream;->close()V\n    :try_end_8\n    .catch Ljava/io/IOException; {:try_start_8 .. :try_end_8} :catch_4\n\n    .line 525\n    :cond_2\n    :goto_5\n    throw v7\n\n    .line 521\n    :catch_4\n    move-exception v3\n\n    .line 522\n    .restart local v3    # \"e\":Ljava/io/IOException;\n    sget-object v8, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v9, \"Issue while closing file stream in message digest computation!\"\n\n    invoke-static {v8, v9}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_5\n\n    .line 521\n    .end local v3    # \"e\":Ljava/io/IOException;\n    .end local v4    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v0    # \"buffer\":[B\n    .restart local v1    # \"digestBytes\":[B\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v6    # \"length\":I\n    :catch_5\n    move-exception v3\n\n    .line 522\n    .restart local v3    # \"e\":Ljava/io/IOException;\n    sget-object v7, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v8, \"Issue while closing file stream in message digest computation!\"\n\n    invoke-static {v7, v8}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .end local v3    # \"e\":Ljava/io/IOException;\n    :cond_3\n    move-object v4, v5\n\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v4    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_1\n\n    .line 517\n    .end local v0    # \"buffer\":[B\n    .end local v1    # \"digestBytes\":[B\n    .end local v4    # \"inStream\":Ljava/io/FileInputStream;\n    .end local v6    # \"length\":I\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    :catchall_1\n    move-exception v7\n\n    move-object v4, v5\n\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v4    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_4\n\n    .line 515\n    .end local v4    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v5    # \"inStream\":Ljava/io/FileInputStream;\n    :catch_6\n    move-exception v3\n\n    move-object v4, v5\n\n    .end local v5    # \"inStream\":Ljava/io/FileInputStream;\n    .restart local v4    # \"inStream\":Ljava/io/FileInputStream;\n    goto :goto_3\n\n    .line 513\n    :catch_7\n    move-exception v3\n\n    goto :goto_2\n.end method\n\n.method private downloadContainerIntoFolder(Ljava/lang/String;Ljava/io/File;)Ljava/lang/String;\n    .locals 16\n    .param p1, \"urlPath\"    # Ljava/lang/String;\n    .param p2, \"resOutputDir\"    # Ljava/io/File;\n\n    .prologue\n    .line 612\n    if-nez p1, :cond_1\n\n    const/4 v11, 0x0\n\n    .line 723\n    :cond_0\n    :goto_0\n    return-object v11\n\n    .line 615\n    :cond_1\n    if-eqz p2, :cond_2\n\n    invoke-virtual/range {p2 .. p2}, Ljava/io/File;->exists()Z\n\n    move-result v13\n\n    if-nez v13, :cond_3\n\n    :cond_2\n    const/4 v11, 0x0\n\n    goto :goto_0\n\n    .line 616\n    :cond_3\n    invoke-virtual/range {p2 .. p2}, Ljava/io/File;->isDirectory()Z\n\n    move-result v13\n\n    if-eqz v13, :cond_4\n\n    invoke-virtual/range {p2 .. p2}, Ljava/io/File;->canRead()Z\n\n    move-result v13\n\n    if-eqz v13, :cond_4\n\n    invoke-virtual/range {p2 .. p2}, Ljava/io/File;->canWrite()Z\n\n    move-result v13\n\n    if-nez v13, :cond_5\n\n    :cond_4\n    const/4 v11, 0x0\n\n    goto :goto_0\n\n    .line 620\n    :cond_5\n    :try_start_0\n    new-instance v12, Ljava/net/URL;\n\n    move-object/from16 v0, p1\n\n    invoke-direct {v12, v0}, Ljava/net/URL;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/net/MalformedURLException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 626\n    .local v12, \"url\":Ljava/net/URL;\n    invoke-virtual {v12}, Ljava/net/URL;->getProtocol()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"http\"\n\n    invoke-virtual {v13, v14}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_6\n\n    invoke-virtual {v12}, Ljava/net/URL;->getProtocol()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"https\"\n\n    invoke-virtual {v13, v14}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_6\n\n    const/4 v11, 0x0\n\n    goto :goto_0\n\n    .line 621\n    .end local v12    # \"url\":Ljava/net/URL;\n    :catch_0\n    move-exception v4\n\n    .line 622\n    .local v4, \"e\":Ljava/net/MalformedURLException;\n    invoke-virtual {v4}, Ljava/net/MalformedURLException;->printStackTrace()V\n\n    .line 623\n    const/4 v11, 0x0\n\n    goto :goto_0\n\n    .line 629\n    .end local v4    # \"e\":Ljava/net/MalformedURLException;\n    .restart local v12    # \"url\":Ljava/net/URL;\n    :cond_6\n    invoke-virtual {v12}, Ljava/net/URL;->getPath()Ljava/lang/String;\n\n    move-result-object v13\n\n    const-string v14, \"/\"\n\n    invoke-virtual {v13, v14}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v9\n\n    .line 630\n    .local v9, \"finalSeparatorIndex\":I\n    invoke-virtual {v12}, Ljava/net/URL;->getFile()Ljava/lang/String;\n\n    move-result-object v13\n\n    invoke-virtual {v13, v9}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v2\n\n    .line 632\n    .local v2, \"containerName\":Ljava/lang/String;\n    if-eqz v2, :cond_7\n\n    invoke-virtual {v2}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v13\n\n    if-eqz v13, :cond_8\n\n    :cond_7\n    const/4 v11, 0x0\n\n    goto :goto_0\n\n    .line 635\n    :cond_8\n    const-string v13, \".\"\n\n    invoke-virtual {v2, v13}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v6\n\n    .line 636\n    .local v6, \"extensionIndex\":I\n    const/4 v5, 0x0\n\n    .line 638\n    .local v5, \"extension\":Ljava/lang/String;\n    const/4 v13, -0x1\n\n    if-eq v6, v13, :cond_9\n\n    .line 640\n    invoke-virtual {v2, v6}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 641\n    const-string v13, \".jar\"\n\n    invoke-virtual {v5, v13}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_9\n\n    const-string v13, \".apk\"\n\n    invoke-virtual {v5, v13}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_9\n\n    const/4 v11, 0x0\n\n    goto/16 :goto_0\n\n    .line 664\n    :cond_9\n    move-object v7, v2\n\n    .line 667\n    .local v7, \"finalContainerName\":Ljava/lang/String;\n    new-instance v1, Ljava/io/File;\n\n    new-instance v13, Ljava/lang/StringBuilder;\n\n    invoke-virtual/range {p2 .. p2}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v14}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-direct {v13, v14}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v13, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v13\n\n    invoke-virtual {v13}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v13\n\n    invoke-direct {v1, v13}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 670\n    .local v1, \"checkFile\":Ljava/io/File;\n    invoke-virtual {v1}, Ljava/io/File;->exists()Z\n\n    move-result v13\n\n    if-eqz v13, :cond_a\n\n    .line 671\n    invoke-virtual {v1}, Ljava/io/File;->delete()Z\n\n    .line 675\n    :cond_a\n    new-instance v13, Ljava/lang/StringBuilder;\n\n    invoke-virtual/range {p2 .. p2}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v14}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-direct {v13, v14}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v13, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v13\n\n    invoke-virtual {v13}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v11\n\n    .line 678\n    .local v11, \"localContainerPath\":Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/SecureLoaderFactory;->mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n    const/4 v14, 0x1\n\n    invoke-virtual {v13, v12, v11, v14}, Lit/necst/grabnrun/FileDownloader;->downloadRemoteUrl(Ljava/net/URL;Ljava/lang/String;Z)Z\n\n    move-result v10\n\n    .line 680\n    .local v10, \"isDownloadSuccessful\":Z\n    if-eqz v10, :cond_f\n\n    .line 685\n    if-nez v5, :cond_0\n\n    .line 688\n    move-object/from16 v0, p0\n\n    iget-object v13, v0, Lit/necst/grabnrun/SecureLoaderFactory;->mFileDownloader:Lit/necst/grabnrun/FileDownloader;\n\n    invoke-virtual {v13}, Lit/necst/grabnrun/FileDownloader;->getDownloadedFileExtension()Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 691\n    if-eqz v5, :cond_f\n\n    const-string v13, \".jar\"\n\n    invoke-virtual {v5, v13}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_b\n\n    const-string v13, \".apk\"\n\n    invoke-virtual {v5, v13}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v13\n\n    if-eqz v13, :cond_f\n\n    .line 694\n    :cond_b\n    new-instance v3, Ljava/io/File;\n\n    invoke-direct {v3, v11}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 695\n    .local v3, \"containerToRename\":Ljava/io/File;\n    new-instance v8, Ljava/io/File;\n\n    new-instance v13, Ljava/lang/StringBuilder;\n\n    invoke-static {v11}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-direct {v13, v14}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v13, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v13\n\n    invoke-virtual {v13}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v13\n\n    invoke-direct {v8, v13}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 697\n    .local v8, \"finalContainerWithExtension\":Ljava/io/File;\n    invoke-virtual {v8}, Ljava/io/File;->exists()Z\n\n    move-result v13\n\n    if-eqz v13, :cond_c\n\n    .line 698\n    invoke-virtual {v8}, Ljava/io/File;->delete()Z\n\n    move-result v13\n\n    if-nez v13, :cond_c\n\n    .line 699\n    sget-object v13, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"Issue while deleting \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v14, v8}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 701\n    :cond_c\n    invoke-virtual {v3, v8}, Ljava/io/File;->renameTo(Ljava/io/File;)Z\n\n    move-result v13\n\n    if-nez v13, :cond_e\n\n    .line 705\n    invoke-virtual {v3}, Ljava/io/File;->delete()Z\n\n    move-result v13\n\n    if-nez v13, :cond_d\n\n    .line 706\n    sget-object v13, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v14, Ljava/lang/StringBuilder;\n\n    const-string v15, \"Issue while deleting \"\n\n    invoke-direct {v14, v15}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v14, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v14\n\n    invoke-virtual {v14}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-static {v13, v14}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 708\n    :cond_d\n    const/4 v11, 0x0\n\n    goto/16 :goto_0\n\n    .line 712\n    :cond_e\n    new-instance v13, Ljava/lang/StringBuilder;\n\n    invoke-static {v11}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v14\n\n    invoke-direct {v13, v14}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v13, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v13\n\n    invoke-virtual {v13}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v11\n\n    goto/16 :goto_0\n\n    .line 723\n    .end local v3    # \"containerToRename\":Ljava/io/File;\n    .end local v8    # \"finalContainerWithExtension\":Ljava/io/File;\n    :cond_f\n    const/4 v11, 0x0\n\n    goto/16 :goto_0\n.end method\n\n.method private sanitizePackageNameToCertificateMap(Ljava/util/Map;)Ljava/util/Map;\n    .locals 14\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;)\",\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;\"\n        }\n    .end annotation\n\n    .prologue\n    .line 534\n    .local p1, \"packageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    if-eqz p1, :cond_0\n\n    invoke-interface {p1}, Ljava/util/Map;->isEmpty()Z\n\n    move-result v9\n\n    if-eqz v9, :cond_2\n\n    :cond_0\n    const/4 v8, 0x0\n\n    .line 606\n    :cond_1\n    return-object v8\n\n    .line 537\n    :cond_2\n    new-instance v8, Ljava/util/LinkedHashMap;\n\n    invoke-direct {v8, p1}, Ljava/util/LinkedHashMap;-><init>(Ljava/util/Map;)V\n\n    .line 540\n    .local v8, \"santiziedPackageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    invoke-interface {v8}, Ljava/util/Map;->keySet()Ljava/util/Set;\n\n    move-result-object v9\n\n    invoke-interface {v9}, Ljava/util/Set;->iterator()Ljava/util/Iterator;\n\n    move-result-object v6\n\n    .line 542\n    .local v6, \"packageNamesIterator\":Ljava/util/Iterator;, \"Ljava/util/Iterator<Ljava/lang/String;>;\"\n    :cond_3\n    :goto_0\n    invoke-interface {v6}, Ljava/util/Iterator;->hasNext()Z\n\n    move-result v9\n\n    if-eqz v9, :cond_1\n\n    .line 544\n    invoke-interface {v6}, Ljava/util/Iterator;->next()Ljava/lang/Object;\n\n    move-result-object v1\n\n    check-cast v1, Ljava/lang/String;\n\n    .line 545\n    .local v1, \"currentPackageName\":Ljava/lang/String;\n    const-string v9, \"\\\\.\"\n\n    invoke-virtual {v1, v9}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 546\n    .local v5, \"packStrings\":[Ljava/lang/String;\n    const/4 v3, 0x1\n\n    .line 547\n    .local v3, \"isValidPackageName\":Z\n    const/4 v7, 0x0\n\n    .line 549\n    .local v7, \"removeThisPackageName\":Z\n    array-length v10, v5\n\n    const/4 v9, 0x0\n\n    :goto_1\n    if-lt v9, v10, :cond_6\n\n    .line 559\n    array-length v9, v5\n\n    const/4 v10, 0x2\n\n    if-ge v9, v10, :cond_4\n\n    .line 560\n    const/4 v7, 0x1\n\n    .line 562\n    :cond_4\n    if-eqz v3, :cond_9\n\n    .line 570\n    :try_start_0\n    invoke-interface {v8, v1}, Ljava/util/Map;->get(Ljava/lang/Object;)Ljava/lang/Object;\n\n    move-result-object v0\n\n    check-cast v0, Ljava/net/URL;\n\n    .line 573\n    .local v0, \"certificateURL\":Ljava/net/URL;\n    if-eqz v0, :cond_5\n\n    .line 575\n    invoke-virtual {v0}, Ljava/net/URL;->getProtocol()Ljava/lang/String;\n\n    move-result-object v9\n\n    const-string v10, \"http\"\n\n    invoke-virtual {v9, v10}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n\n    move-result v9\n\n    if-eqz v9, :cond_8\n\n    .line 578\n    new-instance v9, Ljava/net/URL;\n\n    const-string v10, \"https\"\n\n    invoke-virtual {v0}, Ljava/net/URL;->getHost()Ljava/lang/String;\n\n    move-result-object v11\n\n    invoke-virtual {v0}, Ljava/net/URL;->getPort()I\n\n    move-result v12\n\n    invoke-virtual {v0}, Ljava/net/URL;->getFile()Ljava/lang/String;\n\n    move-result-object v13\n\n    invoke-direct {v9, v10, v11, v12, v13}, Ljava/net/URL;-><init>(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)V\n\n    invoke-interface {v8, v1, v9}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;\n    :try_end_0\n    .catch Ljava/net/MalformedURLException; {:try_start_0 .. :try_end_0} :catch_0\n\n    .line 598\n    .end local v0    # \"certificateURL\":Ljava/net/URL;\n    :cond_5\n    :goto_2\n    if-eqz v7, :cond_3\n\n    .line 601\n    invoke-interface {v6}, Ljava/util/Iterator;->remove()V\n\n    goto :goto_0\n\n    .line 549\n    :cond_6\n    aget-object v4, v5, v9\n\n    .line 552\n    .local v4, \"packString\":Ljava/lang/String;\n    invoke-virtual {v4}, Ljava/lang/String;->isEmpty()Z\n\n    move-result v11\n\n    if-eqz v11, :cond_7\n\n    .line 553\n    const/4 v3, 0x0\n\n    .line 549\n    :cond_7\n    add-int/lit8 v9, v9, 0x1\n\n    goto :goto_1\n\n    .line 581\n    .end local v4    # \"packString\":Ljava/lang/String;\n    .restart local v0    # \"certificateURL\":Ljava/net/URL;\n    :cond_8\n    :try_start_1\n    invoke-virtual {v0}, Ljava/net/URL;->getProtocol()Ljava/lang/String;\n\n    move-result-object v9\n\n    const-string v10, \"https\"\n\n    invoke-virtual {v9, v10}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z\n    :try_end_1\n    .catch Ljava/net/MalformedURLException; {:try_start_1 .. :try_end_1} :catch_0\n\n    move-result v9\n\n    if-nez v9, :cond_5\n\n    .line 584\n    const/4 v7, 0x1\n\n    goto :goto_2\n\n    .line 592\n    .end local v0    # \"certificateURL\":Ljava/net/URL;\n    :catch_0\n    move-exception v2\n\n    .line 593\n    .local v2, \"e\":Ljava/net/MalformedURLException;\n    const/4 v7, 0x1\n\n    .line 595\n    goto :goto_2\n\n    .line 596\n    .end local v2    # \"e\":Ljava/net/MalformedURLException;\n    :cond_9\n    const/4 v7, 0x1\n\n    goto :goto_2\n.end method\n\n\n# virtual methods\n.method public createDexClassLoader(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/Map;)Lit/necst/grabnrun/SecureDexClassLoader;\n    .locals 6\n    .param p1, \"dexPath\"    # Ljava/lang/String;\n    .param p2, \"libraryPath\"    # Ljava/lang/String;\n    .param p3, \"parent\"    # Ljava/lang/ClassLoader;\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/ClassLoader;\",\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;)\",\n            \"Lit/necst/grabnrun/SecureDexClassLoader;\"\n        }\n    .end annotation\n\n    .prologue\n    .line 189\n    .local p4, \"packageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    const/4 v5, 0x0\n\n    move-object v0, p0\n\n    move-object v1, p1\n\n    move-object v2, p2\n\n    move-object v3, p3\n\n    move-object v4, p4\n\n    invoke-virtual/range {v0 .. v5}, Lit/necst/grabnrun/SecureLoaderFactory;->createDexClassLoader(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/Map;Z)Lit/necst/grabnrun/SecureDexClassLoader;\n\n    move-result-object v0\n\n    return-object v0\n.end method\n\n.method public createDexClassLoader(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/Map;Z)Lit/necst/grabnrun/SecureDexClassLoader;\n    .locals 37\n    .param p1, \"dexPath\"    # Ljava/lang/String;\n    .param p2, \"libraryPath\"    # Ljava/lang/String;\n    .param p3, \"parent\"    # Ljava/lang/ClassLoader;\n    .param p5, \"performLazyEvaluation\"    # Z\n    .annotation system Ldalvik/annotation/Signature;\n        value = {\n            \"(\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/String;\",\n            \"Ljava/lang/ClassLoader;\",\n            \"Ljava/util/Map\",\n            \"<\",\n            \"Ljava/lang/String;\",\n            \"Ljava/net/URL;\",\n            \">;Z)\",\n            \"Lit/necst/grabnrun/SecureDexClassLoader;\"\n        }\n    .end annotation\n\n    .prologue\n    .line 249\n    .local p4, \"packageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    new-instance v23, Ljava/lang/StringBuilder;\n\n    invoke-direct/range {v23 .. v23}, Ljava/lang/StringBuilder;-><init>()V\n\n    .line 265\n    .local v23, \"finalDexPath\":Ljava/lang/StringBuilder;\n    const-string v4, \"http://\"\n\n    const-string v5, \"http//\"\n\n    move-object/from16 v0, p1\n\n    invoke-virtual {v0, v4, v5}, Ljava/lang/String;->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v36\n\n    .line 266\n    .local v36, \"tempPath\":Ljava/lang/String;\n    const-string v4, \"https://\"\n\n    const-string v5, \"https//\"\n\n    move-object/from16 v0, v36\n\n    invoke-virtual {v0, v4, v5}, Ljava/lang/String;->replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v36\n\n    .line 271\n    sget-object v4, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-static {v4}, Ljava/util/regex/Pattern;->quote(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v4\n\n    move-object/from16 v0, v36\n\n    invoke-virtual {v0, v4}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;\n\n    move-result-object v35\n\n    .line 275\n    .local v35, \"strings\":[Ljava/lang/String;\n    move-object/from16 v0, p0\n\n    iget-object v4, v0, Lit/necst/grabnrun/SecureLoaderFactory;->mContextWrapper:Landroid/content/ContextWrapper;\n\n    const-string v5, \"imported_cont\"\n\n    const/4 v6, 0x0\n\n    invoke-virtual {v4, v5, v6}, Landroid/content/ContextWrapper;->getDir(Ljava/lang/String;I)Ljava/io/File;\n\n    move-result-object v25\n\n    .line 276\n    .local v25, \"importedContainerDir\":Ljava/io/File;\n    sget-object v4, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v5, Ljava/lang/StringBuilder;\n\n    const-string v6, \"Download Resource Dir has been mounted at: \"\n\n    invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual/range {v25 .. v25}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v6\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v5\n\n    invoke-static {v4, v5}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 278\n    new-instance v29, Lit/necst/grabnrun/CacheLogger;\n\n    invoke-virtual/range {v25 .. v25}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v4\n\n    move-object/from16 v0, p0\n\n    iget v5, v0, Lit/necst/grabnrun/SecureLoaderFactory;->daysBeforeContainerCacheExpiration:I\n\n    move-object/from16 v0, v29\n\n    invoke-direct {v0, v4, v5}, Lit/necst/grabnrun/CacheLogger;-><init>(Ljava/lang/String;I)V\n\n    .line 280\n    .local v29, \"mCacheLogger\":Lit/necst/grabnrun/CacheLogger;\n    move-object/from16 v0, v35\n\n    array-length v5, v0\n\n    const/4 v4, 0x0\n\n    :goto_0\n    if-lt v4, v5, :cond_2\n\n    .line 452\n    sget-object v4, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v4}, Ljava/lang/StringBuilder;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v4\n\n    const/4 v5, -0x1\n\n    if-eq v4, v5, :cond_0\n\n    .line 453\n    sget-object v4, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v4}, Ljava/lang/StringBuilder;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v4\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v4}, Ljava/lang/StringBuilder;->deleteCharAt(I)Ljava/lang/StringBuilder;\n\n    .line 456\n    :cond_0\n    invoke-virtual/range {v29 .. v29}, Lit/necst/grabnrun/CacheLogger;->finalizeLog()V\n\n    .line 463\n    move-object/from16 v0, p0\n\n    iget-object v4, v0, Lit/necst/grabnrun/SecureLoaderFactory;->mContextWrapper:Landroid/content/ContextWrapper;\n\n    const-string v5, \"dex_classes\"\n\n    const/4 v6, 0x0\n\n    invoke-virtual {v4, v5, v6}, Landroid/content/ContextWrapper;->getDir(Ljava/lang/String;I)Ljava/io/File;\n\n    move-result-object v14\n\n    .line 465\n    .local v14, \"dexOutputDir\":Ljava/io/File;\n    sget-object v4, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v5, Ljava/lang/StringBuilder;\n\n    const-string v6, \"Dex Output Dir has been mounted at: \"\n\n    invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v14}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v6\n\n    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v5\n\n    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v5\n\n    invoke-static {v4, v5}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 473\n    move-object/from16 v0, p0\n\n    move-object/from16 v1, p4\n\n    invoke-direct {v0, v1}, Lit/necst/grabnrun/SecureLoaderFactory;->sanitizePackageNameToCertificateMap(Ljava/util/Map;)Ljava/util/Map;\n\n    move-result-object v34\n\n    .line 476\n    .local v34, \"santiziedPackageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    new-instance v3, Lit/necst/grabnrun/SecureDexClassLoader;\n\n    invoke-virtual/range {v23 .. v23}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v4\n\n    .line 477\n    invoke-virtual {v14}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v5\n\n    .line 480\n    move-object/from16 v0, p0\n\n    iget-object v8, v0, Lit/necst/grabnrun/SecureLoaderFactory;->mContextWrapper:Landroid/content/ContextWrapper;\n\n    move-object/from16 v6, p2\n\n    move-object/from16 v7, p3\n\n    move/from16 v9, p5\n\n    .line 476\n    invoke-direct/range {v3 .. v9}, Lit/necst/grabnrun/SecureDexClassLoader;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;Landroid/content/ContextWrapper;Z)V\n\n    .line 484\n    .local v3, \"mSecureDexClassLoader\":Lit/necst/grabnrun/SecureDexClassLoader;\n    if-eqz v3, :cond_1\n\n    move-object/from16 v0, v34\n\n    invoke-virtual {v3, v0}, Lit/necst/grabnrun/SecureDexClassLoader;->setCertificateLocationMap(Ljava/util/Map;)V\n\n    .line 486\n    :cond_1\n    return-object v3\n\n    .line 280\n    .end local v3    # \"mSecureDexClassLoader\":Lit/necst/grabnrun/SecureDexClassLoader;\n    .end local v14    # \"dexOutputDir\":Ljava/io/File;\n    .end local v34    # \"santiziedPackageNameToCertificateMap\":Ljava/util/Map;, \"Ljava/util/Map<Ljava/lang/String;Ljava/net/URL;>;\"\n    :cond_2\n    aget-object v33, v35, v4\n\n    .line 282\n    .local v33, \"path\":Ljava/lang/String;\n    const-string v6, \"http//\"\n\n    move-object/from16 v0, v33\n\n    invoke-virtual {v0, v6}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v6\n\n    if-nez v6, :cond_3\n\n    const-string v6, \"https//\"\n\n    move-object/from16 v0, v33\n\n    invoke-virtual {v0, v6}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v6\n\n    if-eqz v6, :cond_a\n\n    .line 288\n    :cond_3\n    const-string v6, \"http//\"\n\n    move-object/from16 v0, v33\n\n    invoke-virtual {v0, v6}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z\n\n    move-result v6\n\n    if-eqz v6, :cond_5\n\n    .line 289\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    const-string v7, \"http:\"\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const/4 v7, 0x4\n\n    move-object/from16 v0, v33\n\n    invoke-virtual {v0, v7}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v24\n\n    .line 293\n    .local v24, \"fixedRemotePath\":Ljava/lang/String;\n    :goto_1\n    move-object/from16 v0, v29\n\n    move-object/from16 v1, v24\n\n    invoke-virtual {v0, v1}, Lit/necst/grabnrun/CacheLogger;->checkForCachedEntry(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v11\n\n    .line 295\n    .local v11, \"cachedContainerFileName\":Ljava/lang/String;\n    if-eqz v11, :cond_6\n\n    .line 300\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-virtual/range {v25 .. v25}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v7, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6, v11}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    sget-object v7, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    .line 301\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    const-string v8, \"Dex Path has been modified into: \"\n\n    invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v7, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v6, v7}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 280\n    .end local v11    # \"cachedContainerFileName\":Ljava/lang/String;\n    .end local v24    # \"fixedRemotePath\":Ljava/lang/String;\n    :cond_4\n    :goto_2\n    add-int/lit8 v4, v4, 0x1\n\n    goto/16 :goto_0\n\n    .line 291\n    :cond_5\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    const-string v7, \"https:\"\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    const/4 v7, 0x5\n\n    move-object/from16 v0, v33\n\n    invoke-virtual {v0, v7}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v24\n\n    .restart local v24    # \"fixedRemotePath\":Ljava/lang/String;\n    goto :goto_1\n\n    .line 308\n    .restart local v11    # \"cachedContainerFileName\":Ljava/lang/String;\n    :cond_6\n    move-object/from16 v0, p0\n\n    move-object/from16 v1, v24\n\n    move-object/from16 v2, v25\n\n    invoke-direct {v0, v1, v2}, Lit/necst/grabnrun/SecureLoaderFactory;->downloadContainerIntoFolder(Ljava/lang/String;Ljava/io/File;)Ljava/lang/String;\n\n    move-result-object v18\n\n    .line 312\n    .local v18, \"downloadedContainerPath\":Ljava/lang/String;\n    if-eqz v18, :cond_4\n\n    .line 316\n    move-object/from16 v0, p0\n\n    move-object/from16 v1, v18\n\n    invoke-direct {v0, v1}, Lit/necst/grabnrun/SecureLoaderFactory;->computeDigestFromFilePath(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v13\n\n    .line 318\n    .local v13, \"containerDigest\":Ljava/lang/String;\n    new-instance v16, Ljava/io/File;\n\n    move-object/from16 v0, v16\n\n    move-object/from16 v1, v18\n\n    invoke-direct {v0, v1}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 320\n    .local v16, \"downloadedContainer\":Ljava/io/File;\n    if-nez v13, :cond_7\n\n    .line 324\n    invoke-virtual/range {v16 .. v16}, Ljava/io/File;->delete()Z\n\n    move-result v6\n\n    if-nez v6, :cond_4\n\n    .line 325\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    const-string v8, \"Issue while deleting \"\n\n    invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v7, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_2\n\n    .line 329\n    :cond_7\n    const-string v6, \".\"\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v0, v6}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v22\n\n    .line 330\n    .local v22, \"extensionIndex\":I\n    move-object/from16 v0, v18\n\n    move/from16 v1, v22\n\n    invoke-virtual {v0, v1}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v21\n\n    .line 333\n    .local v21, \"extension\":Ljava/lang/String;\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-virtual/range {v25 .. v25}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v7, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6, v13}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    move-object/from16 v0, v21\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v17\n\n    .line 335\n    .local v17, \"downloadedContainerFinalPath\":Ljava/lang/String;\n    new-instance v15, Ljava/io/File;\n\n    move-object/from16 v0, v17\n\n    invoke-direct {v15, v0}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    .line 337\n    .local v15, \"downloadContainerFinalPosition\":Ljava/io/File;\n    invoke-virtual {v15}, Ljava/io/File;->exists()Z\n\n    move-result v6\n\n    if-eqz v6, :cond_8\n\n    .line 338\n    invoke-virtual {v15}, Ljava/io/File;->delete()Z\n\n    move-result v6\n\n    if-nez v6, :cond_8\n\n    .line 339\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    const-string v8, \"Issue while deleting \"\n\n    invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v17\n\n    invoke-virtual {v7, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 341\n    :cond_8\n    move-object/from16 v0, v16\n\n    invoke-virtual {v0, v15}, Ljava/io/File;->renameTo(Ljava/io/File;)Z\n\n    move-result v6\n\n    if-eqz v6, :cond_9\n\n    .line 345\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-static/range {v17 .. v17}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v7, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    .line 346\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    const-string v8, \"Dex Path has been modified into: \"\n\n    invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v7, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/Object;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v6, v7}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I\n\n    .line 349\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-static {v13}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v21\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    move-object/from16 v0, v29\n\n    move-object/from16 v1, v24\n\n    invoke-virtual {v0, v1, v6}, Lit/necst/grabnrun/CacheLogger;->addCachedEntryToLog(Ljava/lang/String;Ljava/lang/String;)V\n\n    goto/16 :goto_2\n\n    .line 353\n    :cond_9\n    invoke-virtual/range {v16 .. v16}, Ljava/io/File;->delete()Z\n\n    move-result v6\n\n    if-nez v6, :cond_4\n\n    .line 354\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    new-instance v7, Ljava/lang/StringBuilder;\n\n    const-string v8, \"Issue while deleting \"\n\n    invoke-direct {v7, v8}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    move-object/from16 v0, v18\n\n    invoke-virtual {v7, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v7\n\n    invoke-virtual {v7}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_2\n\n    .line 379\n    .end local v11    # \"cachedContainerFileName\":Ljava/lang/String;\n    .end local v13    # \"containerDigest\":Ljava/lang/String;\n    .end local v15    # \"downloadContainerFinalPosition\":Ljava/io/File;\n    .end local v16    # \"downloadedContainer\":Ljava/io/File;\n    .end local v17    # \"downloadedContainerFinalPath\":Ljava/lang/String;\n    .end local v18    # \"downloadedContainerPath\":Ljava/lang/String;\n    .end local v21    # \"extension\":Ljava/lang/String;\n    .end local v22    # \"extensionIndex\":I\n    .end local v24    # \"fixedRemotePath\":Ljava/lang/String;\n    :cond_a\n    const/16 v20, 0x0\n\n    .line 382\n    .local v20, \"encodedContainerDigest\":Ljava/lang/String;\n    new-instance v6, Ljava/io/File;\n\n    move-object/from16 v0, v33\n\n    invoke-direct {v6, v0}, Ljava/io/File;-><init>(Ljava/lang/String;)V\n\n    invoke-virtual {v6}, Ljava/io/File;->exists()Z\n\n    move-result v6\n\n    if-eqz v6, :cond_b\n\n    move-object/from16 v0, p0\n\n    move-object/from16 v1, v33\n\n    invoke-direct {v0, v1}, Lit/necst/grabnrun/SecureLoaderFactory;->computeDigestFromFilePath(Ljava/lang/String;)Ljava/lang/String;\n\n    move-result-object v20\n\n    .line 385\n    :cond_b\n    if-eqz v20, :cond_4\n\n    .line 388\n    const-string v6, \".\"\n\n    move-object/from16 v0, v33\n\n    invoke-virtual {v0, v6}, Ljava/lang/String;->lastIndexOf(Ljava/lang/String;)I\n\n    move-result v22\n\n    .line 389\n    .restart local v22    # \"extensionIndex\":I\n    move-object/from16 v0, v33\n\n    move/from16 v1, v22\n\n    invoke-virtual {v0, v1}, Ljava/lang/String;->substring(I)Ljava/lang/String;\n\n    move-result-object v21\n\n    .line 393\n    .restart local v21    # \"extension\":Ljava/lang/String;\n    new-instance v6, Lit/necst/grabnrun/FileFilterByName;\n\n    move-object/from16 v0, v20\n\n    move-object/from16 v1, v21\n\n    invoke-direct {v6, v0, v1}, Lit/necst/grabnrun/FileFilterByName;-><init>(Ljava/lang/String;Ljava/lang/String;)V\n\n    move-object/from16 v0, v25\n\n    invoke-virtual {v0, v6}, Ljava/io/File;->listFiles(Ljava/io/FileFilter;)[Ljava/io/File;\n\n    move-result-object v30\n\n    .line 395\n    .local v30, \"matchingContainerArray\":[Ljava/io/File;\n    if-eqz v30, :cond_c\n\n    move-object/from16 v0, v30\n\n    array-length v6, v0\n\n    if-lez v6, :cond_c\n\n    .line 399\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    const/4 v7, 0x0\n\n    aget-object v7, v30, v7\n\n    invoke-virtual {v7}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v7, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    goto/16 :goto_2\n\n    .line 406\n    :cond_c\n    const/16 v26, 0x0\n\n    .line 407\n    .local v26, \"inStream\":Ljava/io/InputStream;\n    const/16 v31, 0x0\n\n    .line 408\n    .local v31, \"outStream\":Ljava/io/OutputStream;\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-virtual/range {v25 .. v25}, Ljava/io/File;->getAbsolutePath()Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v7, Ljava/io/File;->separator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    move-object/from16 v0, v20\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    move-object/from16 v0, v21\n\n    invoke-virtual {v6, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v12\n\n    .line 412\n    .local v12, \"cachedContainerPath\":Ljava/lang/String;\n    :try_start_0\n    new-instance v27, Ljava/io/FileInputStream;\n\n    move-object/from16 v0, v27\n\n    move-object/from16 v1, v33\n\n    invoke-direct {v0, v1}, Ljava/io/FileInputStream;-><init>(Ljava/lang/String;)V\n    :try_end_0\n    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_8\n    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_3\n    .catchall {:try_start_0 .. :try_end_0} :catchall_0\n\n    .line 413\n    .end local v26    # \"inStream\":Ljava/io/InputStream;\n    .local v27, \"inStream\":Ljava/io/InputStream;\n    :try_start_1\n    new-instance v32, Ljava/io/FileOutputStream;\n\n    move-object/from16 v0, v32\n\n    invoke-direct {v0, v12}, Ljava/io/FileOutputStream;-><init>(Ljava/lang/String;)V\n    :try_end_1\n    .catch Ljava/io/FileNotFoundException; {:try_start_1 .. :try_end_1} :catch_9\n    .catch Ljava/io/IOException; {:try_start_1 .. :try_end_1} :catch_6\n    .catchall {:try_start_1 .. :try_end_1} :catchall_1\n\n    .line 415\n    .end local v31    # \"outStream\":Ljava/io/OutputStream;\n    .local v32, \"outStream\":Ljava/io/OutputStream;\n    const/16 v6, 0x2000\n\n    :try_start_2\n    new-array v10, v6, [B\n\n    .line 420\n    .local v10, \"buf\":[B\n    :goto_3\n    move-object/from16 v0, v27\n\n    invoke-virtual {v0, v10}, Ljava/io/InputStream;->read([B)I\n\n    move-result v28\n\n    .local v28, \"length\":I\n    if-gtz v28, :cond_e\n\n    .line 425\n    new-instance v6, Ljava/lang/StringBuilder;\n\n    invoke-static {v12}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;\n\n    move-result-object v7\n\n    invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V\n\n    sget-object v7, Ljava/io/File;->pathSeparator:Ljava/lang/String;\n\n    invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n\n    move-result-object v6\n\n    invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;\n\n    move-result-object v6\n\n    move-object/from16 v0, v23\n\n    invoke-virtual {v0, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;\n    :try_end_2\n    .catch Ljava/io/FileNotFoundException; {:try_start_2 .. :try_end_2} :catch_1\n    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_7\n    .catchall {:try_start_2 .. :try_end_2} :catchall_2\n\n    .line 435\n    if-eqz v27, :cond_d\n\n    .line 436\n    :try_start_3\n    invoke-virtual/range {v27 .. v27}, Ljava/io/InputStream;->close()V\n\n    .line 438\n    :cond_d\n    if-eqz v32, :cond_4\n\n    .line 439\n    invoke-virtual/range {v32 .. v32}, Ljava/io/OutputStream;->close()V\n    :try_end_3\n    .catch Ljava/io/IOException; {:try_start_3 .. :try_end_3} :catch_0\n\n    goto/16 :goto_2\n\n    .line 442\n    :catch_0\n    move-exception v19\n\n    .line 443\n    .local v19, \"e\":Ljava/io/IOException;\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v7, \"Issue in closing file streams while importing a container!\"\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_2\n\n    .line 421\n    .end local v19    # \"e\":Ljava/io/IOException;\n    :cond_e\n    const/4 v6, 0x0\n\n    :try_start_4\n    move-object/from16 v0, v32\n\n    move/from16 v1, v28\n\n    invoke-virtual {v0, v10, v6, v1}, Ljava/io/OutputStream;->write([BII)V\n    :try_end_4\n    .catch Ljava/io/FileNotFoundException; {:try_start_4 .. :try_end_4} :catch_1\n    .catch Ljava/io/IOException; {:try_start_4 .. :try_end_4} :catch_7\n    .catchall {:try_start_4 .. :try_end_4} :catchall_2\n\n    goto :goto_3\n\n    .line 427\n    .end local v10    # \"buf\":[B\n    .end local v28    # \"length\":I\n    :catch_1\n    move-exception v19\n\n    move-object/from16 v31, v32\n\n    .end local v32    # \"outStream\":Ljava/io/OutputStream;\n    .restart local v31    # \"outStream\":Ljava/io/OutputStream;\n    move-object/from16 v26, v27\n\n    .line 428\n    .end local v27    # \"inStream\":Ljava/io/InputStream;\n    .local v19, \"e\":Ljava/io/FileNotFoundException;\n    .restart local v26    # \"inStream\":Ljava/io/InputStream;\n    :goto_4\n    :try_start_5\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v7, \"Problem in locating container to import in the application private folder!\"\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_5\n    .catchall {:try_start_5 .. :try_end_5} :catchall_0\n\n    .line 435\n    if-eqz v26, :cond_f\n\n    .line 436\n    :try_start_6\n    invoke-virtual/range {v26 .. v26}, Ljava/io/InputStream;->close()V\n\n    .line 438\n    :cond_f\n    if-eqz v31, :cond_4\n\n    .line 439\n    invoke-virtual/range {v31 .. v31}, Ljava/io/OutputStream;->close()V\n    :try_end_6\n    .catch Ljava/io/IOException; {:try_start_6 .. :try_end_6} :catch_2\n\n    goto/16 :goto_2\n\n    .line 442\n    :catch_2\n    move-exception v19\n\n    .line 443\n    .local v19, \"e\":Ljava/io/IOException;\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v7, \"Issue in closing file streams while importing a container!\"\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_2\n\n    .line 429\n    .end local v19    # \"e\":Ljava/io/IOException;\n    :catch_3\n    move-exception v19\n\n    .line 430\n    .restart local v19    # \"e\":Ljava/io/IOException;\n    :goto_5\n    :try_start_7\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v7, \"Problem while importing a local container into the application private folder!\"\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n    :try_end_7\n    .catchall {:try_start_7 .. :try_end_7} :catchall_0\n\n    .line 435\n    if-eqz v26, :cond_10\n\n    .line 436\n    :try_start_8\n    invoke-virtual/range {v26 .. v26}, Ljava/io/InputStream;->close()V\n\n    .line 438\n    :cond_10\n    if-eqz v31, :cond_4\n\n    .line 439\n    invoke-virtual/range {v31 .. v31}, Ljava/io/OutputStream;->close()V\n    :try_end_8\n    .catch Ljava/io/IOException; {:try_start_8 .. :try_end_8} :catch_4\n\n    goto/16 :goto_2\n\n    .line 442\n    :catch_4\n    move-exception v19\n\n    .line 443\n    sget-object v6, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v7, \"Issue in closing file streams while importing a container!\"\n\n    invoke-static {v6, v7}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto/16 :goto_2\n\n    .line 431\n    .end local v19    # \"e\":Ljava/io/IOException;\n    :catchall_0\n    move-exception v4\n\n    .line 435\n    :goto_6\n    if-eqz v26, :cond_11\n\n    .line 436\n    :try_start_9\n    invoke-virtual/range {v26 .. v26}, Ljava/io/InputStream;->close()V\n\n    .line 438\n    :cond_11\n    if-eqz v31, :cond_12\n\n    .line 439\n    invoke-virtual/range {v31 .. v31}, Ljava/io/OutputStream;->close()V\n    :try_end_9\n    .catch Ljava/io/IOException; {:try_start_9 .. :try_end_9} :catch_5\n\n    .line 445\n    :cond_12\n    :goto_7\n    throw v4\n\n    .line 442\n    :catch_5\n    move-exception v19\n\n    .line 443\n    .restart local v19    # \"e\":Ljava/io/IOException;\n    sget-object v5, Lit/necst/grabnrun/SecureLoaderFactory;->TAG_SECURE_FACTORY:Ljava/lang/String;\n\n    const-string v6, \"Issue in closing file streams while importing a container!\"\n\n    invoke-static {v5, v6}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I\n\n    goto :goto_7\n\n    .line 431\n    .end local v19    # \"e\":Ljava/io/IOException;\n    .end local v26    # \"inStream\":Ljava/io/InputStream;\n    .restart local v27    # \"inStream\":Ljava/io/InputStream;\n    :catchall_1\n    move-exception v4\n\n    move-object/from16 v26, v27\n\n    .end local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v26    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_6\n\n    .end local v26    # \"inStream\":Ljava/io/InputStream;\n    .end local v31    # \"outStream\":Ljava/io/OutputStream;\n    .restart local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v32    # \"outStream\":Ljava/io/OutputStream;\n    :catchall_2\n    move-exception v4\n\n    move-object/from16 v31, v32\n\n    .end local v32    # \"outStream\":Ljava/io/OutputStream;\n    .restart local v31    # \"outStream\":Ljava/io/OutputStream;\n    move-object/from16 v26, v27\n\n    .end local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v26    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_6\n\n    .line 429\n    .end local v26    # \"inStream\":Ljava/io/InputStream;\n    .restart local v27    # \"inStream\":Ljava/io/InputStream;\n    :catch_6\n    move-exception v19\n\n    move-object/from16 v26, v27\n\n    .end local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v26    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_5\n\n    .end local v26    # \"inStream\":Ljava/io/InputStream;\n    .end local v31    # \"outStream\":Ljava/io/OutputStream;\n    .restart local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v32    # \"outStream\":Ljava/io/OutputStream;\n    :catch_7\n    move-exception v19\n\n    move-object/from16 v31, v32\n\n    .end local v32    # \"outStream\":Ljava/io/OutputStream;\n    .restart local v31    # \"outStream\":Ljava/io/OutputStream;\n    move-object/from16 v26, v27\n\n    .end local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v26    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_5\n\n    .line 427\n    :catch_8\n    move-exception v19\n\n    goto :goto_4\n\n    .end local v26    # \"inStream\":Ljava/io/InputStream;\n    .restart local v27    # \"inStream\":Ljava/io/InputStream;\n    :catch_9\n    move-exception v19\n\n    move-object/from16 v26, v27\n\n    .end local v27    # \"inStream\":Ljava/io/InputStream;\n    .restart local v26    # \"inStream\":Ljava/io/InputStream;\n    goto :goto_4\n.end method\n"
  }
]