Full Code of LSPosed/LSPatch for AI

master bbe8d93fb923 cached
260 files
1.1 MB
275.5k tokens
841 symbols
1 requests
Download .txt
Showing preview only (1,167K chars total). Download the full file or copy to clipboard to get everything.
Repository: LSPosed/LSPatch
Branch: master
Commit: bbe8d93fb923
Files: 260
Total size: 1.1 MB

Directory structure:
gitextract_ca_6zfem/

├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   └── workflows/
│       ├── crowdin.yml
│       └── main.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── apkzlib/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── android/
│                       └── tools/
│                           └── build/
│                               └── apkzlib/
│                                   ├── bytestorage/
│                                   │   ├── AbstractCloseableByteSourceFromOutputStreamBuilder.java
│                                   │   ├── ByteStorage.java
│                                   │   ├── ByteStorageFactory.java
│                                   │   ├── ChunkBasedByteStorage.java
│                                   │   ├── ChunkBasedByteStorageFactory.java
│                                   │   ├── ChunkBasedCloseableByteSource.java
│                                   │   ├── CloseableByteSourceFromOutputStreamBuilder.java
│                                   │   ├── InMemoryByteStorage.java
│                                   │   ├── InMemoryByteStorageFactory.java
│                                   │   ├── LimitedInputStream.java
│                                   │   ├── LruTrackedCloseableByteSource.java
│                                   │   ├── LruTracker.java
│                                   │   ├── OverflowToDiskByteStorage.java
│                                   │   ├── OverflowToDiskByteStorageFactory.java
│                                   │   ├── SwitchableDelegateCloseableByteSource.java
│                                   │   ├── SwitchableDelegateInputStream.java
│                                   │   ├── TemporaryDirectory.java
│                                   │   ├── TemporaryDirectoryFactory.java
│                                   │   ├── TemporaryDirectoryStorage.java
│                                   │   ├── TemporaryFile.java
│                                   │   └── TemporaryFileCloseableByteSource.java
│                                   ├── sign/
│                                   │   ├── DigestAlgorithm.java
│                                   │   ├── ManifestGenerationExtension.java
│                                   │   ├── SignatureAlgorithm.java
│                                   │   ├── SigningExtension.java
│                                   │   ├── SigningOptions.java
│                                   │   └── package-info.java
│                                   ├── utils/
│                                   │   ├── ApkZLibPair.java
│                                   │   ├── CachedFileContents.java
│                                   │   ├── CachedSupplier.java
│                                   │   ├── IOExceptionConsumer.java
│                                   │   ├── IOExceptionFunction.java
│                                   │   ├── IOExceptionRunnable.java
│                                   │   ├── IOExceptionWrapper.java
│                                   │   ├── SigningBlockUtils.java
│                                   │   └── package-info.java
│                                   ├── zfile/
│                                   │   ├── ApkCreator.java
│                                   │   ├── ApkCreatorFactory.java
│                                   │   ├── ApkZFileCreator.java
│                                   │   ├── ApkZFileCreatorFactory.java
│                                   │   ├── ManifestAttributes.java
│                                   │   ├── NativeLibrariesPackagingMode.java
│                                   │   ├── ZFiles.java
│                                   │   └── package-info.java
│                                   └── zip/
│                                       ├── AlignmentRule.java
│                                       ├── AlignmentRules.java
│                                       ├── CentralDirectory.java
│                                       ├── CentralDirectoryHeader.java
│                                       ├── CentralDirectoryHeaderCompressInfo.java
│                                       ├── CompressionMethod.java
│                                       ├── CompressionResult.java
│                                       ├── Compressor.java
│                                       ├── DataDescriptorType.java
│                                       ├── EncodeUtils.java
│                                       ├── Eocd.java
│                                       ├── EocdGroup.java
│                                       ├── ExtraField.java
│                                       ├── FileUseMap.java
│                                       ├── FileUseMapEntry.java
│                                       ├── GPFlags.java
│                                       ├── InflaterByteSource.java
│                                       ├── LazyDelegateByteSource.java
│                                       ├── NestedZip.java
│                                       ├── ProcessedAndRawByteSources.java
│                                       ├── StoredEntry.java
│                                       ├── StoredEntryType.java
│                                       ├── VerifyLog.java
│                                       ├── VerifyLogs.java
│                                       ├── ZFile.java
│                                       ├── ZFileExtension.java
│                                       ├── ZFileOptions.java
│                                       ├── Zip64Eocd.java
│                                       ├── Zip64EocdLocator.java
│                                       ├── Zip64ExtensibleDataSector.java
│                                       ├── ZipField.java
│                                       ├── ZipFieldInvariant.java
│                                       ├── ZipFieldInvariantMaxValue.java
│                                       ├── ZipFieldInvariantMinValue.java
│                                       ├── ZipFieldInvariantNonNegative.java
│                                       ├── ZipFileState.java
│                                       ├── compress/
│                                       │   ├── BestAndDefaultDeflateExecutorCompressor.java
│                                       │   ├── DeflateExecutionCompressor.java
│                                       │   ├── ExecutorCompressor.java
│                                       │   ├── Zip64NotSupportedException.java
│                                       │   └── package-info.java
│                                       └── utils/
│                                           ├── ByteTracker.java
│                                           ├── CloseableByteSource.java
│                                           ├── CloseableDelegateByteSource.java
│                                           ├── LittleEndianUtils.java
│                                           ├── MsDosDateTimeUtils.java
│                                           └── RandomAccessFileUtils.java
├── build.gradle.kts
├── crowdin.yml
├── gradle/
│   ├── lspatch.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jar/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── assets/
│               └── keystore
├── manager/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules-debug.pro
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── keystore
│           ├── java/
│           │   └── org/
│           │       └── lsposed/
│           │           └── lspatch/
│           │               ├── LSPApplication.kt
│           │               ├── Patcher.kt
│           │               ├── config/
│           │               │   ├── ConfigManager.kt
│           │               │   ├── Configs.kt
│           │               │   └── MyKeyStore.kt
│           │               ├── database/
│           │               │   ├── LSPDatabase.kt
│           │               │   ├── dao/
│           │               │   │   ├── ModuleDao.kt
│           │               │   │   └── ScopeDao.kt
│           │               │   └── entity/
│           │               │       ├── Module.kt
│           │               │       └── Scope.kt
│           │               ├── manager/
│           │               │   ├── AppBroadcastReceiver.kt
│           │               │   ├── ManagerService.kt
│           │               │   └── ModuleService.kt
│           │               ├── ui/
│           │               │   ├── activity/
│           │               │   │   └── MainActivity.kt
│           │               │   ├── component/
│           │               │   │   ├── AnywhereDropdown.kt
│           │               │   │   ├── AppItem.kt
│           │               │   │   ├── CenterTopBar.kt
│           │               │   │   ├── LoadingDialog.kt
│           │               │   │   ├── SearchBar.kt
│           │               │   │   ├── SelectionColumn.kt
│           │               │   │   ├── Shimmer.kt
│           │               │   │   └── settings/
│           │               │   │       ├── CheckBox.kt
│           │               │   │       ├── Slot.kt
│           │               │   │       └── Switch.kt
│           │               │   ├── page/
│           │               │   │   ├── BottomBarDestination.kt
│           │               │   │   ├── HomeScreen.kt
│           │               │   │   ├── LogsScreen.kt
│           │               │   │   ├── ManageScreen.kt
│           │               │   │   ├── NewPatchScreen.kt
│           │               │   │   ├── RepoScreen.kt
│           │               │   │   ├── SelectAppsScreen.kt
│           │               │   │   ├── SettingsScreen.kt
│           │               │   │   └── manage/
│           │               │   │       ├── AppManagePage.kt
│           │               │   │       └── ModuleManagePage.kt
│           │               │   ├── theme/
│           │               │   │   ├── Theme.kt
│           │               │   │   └── Type.kt
│           │               │   ├── util/
│           │               │   │   ├── CompositionProvider.kt
│           │               │   │   ├── HtmlText.kt
│           │               │   │   ├── MultiDelegateState.kt
│           │               │   │   ├── Preview.kt
│           │               │   │   └── Utils.kt
│           │               │   ├── viewmodel/
│           │               │   │   ├── NewPatchViewModel.kt
│           │               │   │   ├── SelectAppsViewModel.kt
│           │               │   │   └── manage/
│           │               │   │       ├── AppManageViewModel.kt
│           │               │   │       └── ModuleManageViewModel.kt
│           │               │   └── viewstate/
│           │               │       └── ProcessingState.kt
│           │               └── util/
│           │                   ├── IntentSenderHelper.kt
│           │                   ├── LSPPackageManager.kt
│           │                   └── ShizukuApi.kt
│           └── res/
│               ├── drawable/
│               │   ├── ic_launcher_background.xml
│               │   └── ic_launcher_foreground.xml
│               ├── drawable-zh-rCN/
│               │   └── ic_launcher_background.xml
│               ├── drawable-zh-rTW/
│               │   └── ic_launcher_background.xml
│               ├── mipmap-anydpi-v26/
│               │   └── ic_launcher.xml
│               ├── values/
│               │   ├── strings.xml
│               │   └── strings_untranslatable.xml
│               ├── values-af/
│               │   └── strings.xml
│               ├── values-ar/
│               │   └── strings.xml
│               ├── values-bg/
│               │   └── strings.xml
│               ├── values-bn/
│               │   └── strings.xml
│               ├── values-ca/
│               │   └── strings.xml
│               ├── values-cs/
│               │   └── strings.xml
│               ├── values-da/
│               │   └── strings.xml
│               ├── values-de/
│               │   └── strings.xml
│               ├── values-el/
│               │   └── strings.xml
│               ├── values-es/
│               │   └── strings.xml
│               ├── values-et/
│               │   └── strings.xml
│               ├── values-fa/
│               │   └── strings.xml
│               ├── values-fi/
│               │   └── strings.xml
│               ├── values-fr/
│               │   └── strings.xml
│               ├── values-hi/
│               │   └── strings.xml
│               ├── values-hr/
│               │   └── strings.xml
│               ├── values-hu/
│               │   └── strings.xml
│               ├── values-in/
│               │   └── strings.xml
│               ├── values-it/
│               │   └── strings.xml
│               ├── values-iw/
│               │   └── strings.xml
│               ├── values-ja/
│               │   └── strings.xml
│               ├── values-ko/
│               │   └── strings.xml
│               ├── values-ku/
│               │   └── strings.xml
│               ├── values-lt/
│               │   └── strings.xml
│               ├── values-nl/
│               │   └── strings.xml
│               ├── values-no/
│               │   └── strings.xml
│               ├── values-pl/
│               │   └── strings.xml
│               ├── values-pt/
│               │   └── strings.xml
│               ├── values-pt-rBR/
│               │   └── strings.xml
│               ├── values-ro/
│               │   └── strings.xml
│               ├── values-ru/
│               │   └── strings.xml
│               ├── values-si/
│               │   └── strings.xml
│               ├── values-sk/
│               │   └── strings.xml
│               ├── values-sv/
│               │   └── strings.xml
│               ├── values-th/
│               │   └── strings.xml
│               ├── values-tr/
│               │   └── strings.xml
│               ├── values-uk/
│               │   └── strings.xml
│               ├── values-ur/
│               │   └── strings.xml
│               ├── values-vi/
│               │   └── strings.xml
│               ├── values-zh-rCN/
│               │   └── strings.xml
│               ├── values-zh-rHK/
│               │   └── strings.xml
│               └── values-zh-rTW/
│                   └── strings.xml
├── meta-loader/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── java/
│               └── org/
│                   └── lsposed/
│                       └── lspatch/
│                           └── metaloader/
│                               └── LSPAppComponentFactoryStub.java
├── patch/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── java/
│               └── org/
│                   └── lsposed/
│                       └── patch/
│                           ├── LSPatch.java
│                           └── util/
│                               ├── ApkSignatureHelper.java
│                               ├── JavaLogger.java
│                               ├── Logger.java
│                               └── ManifestParser.java
├── patch-loader/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── org/
│           │       └── lsposed/
│           │           ├── lspatch/
│           │           │   ├── loader/
│           │           │   │   ├── LSPApplication.java
│           │           │   │   ├── LSPLoader.java
│           │           │   │   ├── SigBypass.java
│           │           │   │   └── util/
│           │           │   │       ├── FileUtils.java
│           │           │   │       └── XLog.java
│           │           │   └── service/
│           │           │       ├── LocalApplicationService.java
│           │           │       └── RemoteApplicationService.java
│           │           └── lspd/
│           │               └── nativebridge/
│           │                   └── SigBypass.java
│           └── jni/
│               ├── CMakeLists.txt
│               ├── api/
│               │   └── patch_main.cpp
│               ├── include/
│               │   └── art/
│               │       └── runtime/
│               │           ├── jit/
│               │           │   └── profile_saver.h
│               │           └── oat_file_manager.h
│               └── src/
│                   ├── config_impl.h
│                   ├── jni/
│                   │   ├── bypass_sig.cpp
│                   │   └── bypass_sig.h
│                   ├── patch_loader.cpp
│                   └── patch_loader.h
├── settings.gradle.kts
└── share/
    ├── android/
    │   ├── .gitignore
    │   ├── build.gradle.kts
    │   └── src/
    │       └── main/
    │           └── java/
    │               └── org/
    │                   └── lsposed/
    │                       └── lspatch/
    │                           └── util/
    │                               └── ModuleLoader.java
    └── java/
        ├── .gitignore
        ├── build.gradle.kts
        └── src/
            ├── main/
            │   └── java/
            │       └── org/
            │           └── lsposed/
            │               └── lspatch/
            │                   └── share/
            │                       ├── Constants.java
            │                       └── PatchConfig.java
            └── template/
                └── java/
                    └── org.lsposed.lspatch.share/
                        └── LSPConfig.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto eol=lf

# Declare files that will always have CRLF line endings on checkout.
*.cmd text eol=crlf
*.bat text eol=crlf

# Denote all files that are truly binary and should not be modified.
*.so binary
*.dex binary


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug report/反馈 Bug
description: Report errors or unexpected behavior./反馈错误或异常行为。
labels: [bug]
title: "[Bug] "
body:
  - type: markdown
    attributes:
      value: |
        Thanks for reporting issues of LSPatch!

        To make it easier for us to help you please enter detailed information below.
        
        感谢给 LSPatch 汇报问题!
        为了使我们更好地帮助你,请提供以下信息。
        为了防止重复汇报,标题请务必使用英文。
  - type: textarea
    attributes:
      label: Steps to reproduce/复现步骤
      value: |
        1. 
        1. 
        1. 
    validations:
      required: true
  - type: textarea
    attributes:
      label: Expected behaviour/预期行为
      placeholder: Tell us what should happen/正常情况下应该发生什么
    validations:
      required: true
  - type: textarea
    attributes:
      label: Actual behaviour/实际行为
      placeholder: Tell us what happens instead/实际上发生了什么
    validations:
      required: true
  - type: textarea
    attributes:
      label: Xposed Module List/Xposed 模块列表
      render: Shell
    validations:
      required: true
  - type: input
    attributes:
      label: LSPatch version/LSPatch 版本
      description:  Don't use 'latest'. Specify actual version with 4 digits, otherwise your issue will be closed./不要填用“最新版”。给出四位版本号,不然 issue 会被关闭。
    validations:
      required: true
  - type: input
    attributes:
      label: Android version/Android 版本
    validations:
      required: true
  - type: input
    attributes:
      label: Shizuku version/Shizuku 版本
      description: If you are not using Shizuku mode, input `N/A` instead./如果未使用 Shizuku 模式,请键入 `N/A`。
    validations:
      required: true
  - type: checkboxes
    id: latest
    attributes:
      label: Version requirement/版本要求
      options:
        - label: I am using latest debug CI version of LSPatch and enable verbose log/我正在使用最新 CI 调试版本且启用详细日志
          required: true
  - type: textarea
    attributes:
      label: Apk file/Apk 文件
      description: The apk file if patching failed / 修复失败的 apk 文件(如有)
      placeholder: Upload apks by clicking the bar on the bottom. /点击文本框底栏上传安装包文件。
  - type: textarea
    attributes:
      label: Logs/日志
      description: For usage issues, please provide the log zip saved from manager; for activation issues, please provide [bugreport](https://developer.android.com/studio/debug/bug-report). Without log, the issue will be closed. /使用问题请提供从管理器保存的日志压缩包;激活问题请提供 [bugreport](https://developer.android.google.cn/studio/debug/bug-report?hl=zh-cn) 日志。无日志提交会被关闭。
      value: |
        <details>
        
        ```
        # Replace this line with the log / 将此行用日志替换
        ```
        </details>
    validations:
      required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Ask a question/提问
    url: https://github.com/LSPosed/LSPatch/discussions/new?category=Q-A
    about: Please ask and answer questions here./如果有任何疑问请在这里提问
  - name: Official Telegram Channel/官方 Telegram 频道
    url: https://t.me/LSPosed
    about: Subscribe for notifications and releases/可以订阅通知和发行版


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
---
name: Feature request/新特性请求
description: Suggest an idea./提出建议
labels: [enhancement]
title: "[Feature Request] "
body:
  - type: textarea
    attributes:
      label: Is your feature request related to a problem?/你的请求是否与某个问题相关?
      placeholder: A clear and concise description of what the problem is./请清晰准确表述该问题。
    validations:
      required: true
  - type: textarea
    attributes:
      label: Describe the solution you'd like/描述你想要的解决方案
      placeholder: A clear and concise description of what you want to happen./请清晰准确描述新特性的预期行为
    validations:
      required: true
  - type: textarea
    attributes:
      label: Additional context/其他信息
      placeholder: Add any other context or screenshots about the feature request here./其他关于新特性的信息或者截图
    validations:
      required: false


================================================
FILE: .github/workflows/crowdin.yml
================================================
name: Crowdin Action

on:
  workflow_dispatch:
  push:
    branches: [ master ]
    paths:
    - manager/src/main/res/values/strings.xml

jobs:
  synchronize-with-crowdin:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: crowdin action
      uses: crowdin/github-action@master
      with:
        upload_translations: false
        download_translations: false
        upload_sources: true
        config: 'crowdin.yml'
        crowdin_branch_name: master
      env:
        CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
        CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}


================================================
FILE: .github/workflows/main.yml
================================================
name: Android CI

on:
  workflow_dispatch:
  push:
    branches: [ master ]
  pull_request:

jobs:
  build:
    name: Build on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ ubuntu-latest ]
    env:
      CCACHE_COMPILERCHECK: '%compiler% -dumpmachine; %compiler% -dumpversion'
      CCACHE_NOHASHDIR: 'true'
      CCACHE_HARDLINK: 'true'
      CCACHE_BASEDIR: '${{ github.workspace }}'

    steps:
    - name: Checkout
      uses: actions/checkout@v3
      with:
        submodules: 'recursive'
        fetch-depth: 0

    - name: Write key
      if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master'
      run: |
        if [ ! -z "${{ secrets.KEY_STORE }}" ]; then
          echo androidStorePassword='${{ secrets.KEY_STORE_PASSWORD }}' >> gradle.properties
          echo androidKeyAlias='${{ secrets.ALIAS }}' >> gradle.properties
          echo androidKeyPassword='${{ secrets.KEY_PASSWORD }}' >> gradle.properties
          echo androidStoreFile='key.jks' >> gradle.properties
          echo ${{ secrets.KEY_STORE }} | base64 --decode > key.jks
        fi

    - name: Checkout libxposed/api
      uses: actions/checkout@v3
      with:
        repository: libxposed/api
        path: libxposed/api

    - name: Checkout libxposed/service
      uses: actions/checkout@v3
      with:
        repository: libxposed/service
        path: libxposed/service

    - name: Setup Java
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Setup Gradle
      uses: gradle/gradle-build-action@v2
      with:
        gradle-home-cache-cleanup: true

    - name: Set up ccache
      uses: hendrikmuhs/ccache-action@v1.2
      with:
        max-size: 2G
        key: ${{ runner.os }}
        restore-keys: ${{ runner.os }}
        save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}

    - name: Build dependencies
      working-directory: libxposed
      run: |
        cd api
        echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
        ./gradlew :api:publishApiPublicationToMavenLocal
        cd ..
        cd service
        echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
        ./gradlew :interface:publishInterfacePublicationToMavenLocal

    - name: Build with Gradle
      run: |
        echo 'org.gradle.parallel=true' >> gradle.properties
        echo 'org.gradle.jvmargs=-Xmx2048m' >> gradle.properties
        echo 'android.native.buildOutput=verbose' >> gradle.properties
        ./gradlew buildAll

    - name: Upload Debug artifact
      uses: actions/upload-artifact@v3
      with:
        name: lspatch-debug
        path: out/debug/*

    - name: Upload Release artifact
      uses: actions/upload-artifact@v3
      with:
        name: lspatch-release
        path: out/release/*

    - name: Upload mappings
      uses: actions/upload-artifact@v3
      with:
        name: mappings
        path: |
          patch-loader/build/outputs/mapping
          manager/build/outputs/mapping

    - name: Upload symbols
      uses: actions/upload-artifact@v3
      with:
        name: symbols
        path: |
          patch-loader/build/symbols

    - name: Post to channel
      if: ${{ github.event_name != 'pull_request' && success() && github.ref == 'refs/heads/master' }}
      env:
        CHANNEL_ID: ${{ secrets.CHANNEL_ID }}
        DISCUSSION_ID: ${{ secrets.DISCUSSION_ID }}
        TOPIC_ID: ${{ secrets.TOPIC_ID }}
        BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
        COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
        COMMIT_URL: ${{ github.event.head_commit.url }}
      run: |
        if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
          export jarRelease=$(find out/release -name "*.jar")
          export managerRelease=$(find out/release -name "*.apk")
          export jarDebug=$(find out/debug -name "*.jar")
          export managerDebug=$(find out/debug -name "*.apk")
          ESCAPED=`python3 -c 'import json,os,urllib.parse; msg = json.dumps(os.environ["COMMIT_MESSAGE"]); print(urllib.parse.quote(msg if len(msg) <= 1024 else json.dumps(os.environ["COMMIT_URL"])))'`
          curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${CHANNEL_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FjarRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FmanagerRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FjarDebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FmanagerDebug%22%2C%22caption%22:${ESCAPED}%7D%5D" -F jarRelease="@$jarRelease" -F managerRelease="@$managerRelease" -F jarDebug="@$jarDebug" -F managerDebug="@$managerDebug"
          # curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${DISCUSSION_ID}&message_thread_id=${TOPIC_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FjarRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FmanagerRelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FjarDebug%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2FmanagerDebug%22%2C%22caption%22:${ESCAPED}%7D%5D" -F jarRelease="@$jarRelease" -F managerRelease="@$managerRelease" -F jarDebug="@$jarDebug" -F managerDebug="@$managerDebug"
        fi


================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
/out
/.idea


================================================
FILE: .gitmodules
================================================
[submodule "apksigner"]
	path = apksigner
	url = https://android.googlesource.com/platform/tools/apksig.git
	branch = android10-release
[submodule "core"]
	path = core
	url = https://github.com/LSPosed/LSPosed.git
	branch = master
[submodule "patch/libs/manifest-editor"]
	path = patch/libs/manifest-editor
	url = https://github.com/WindySha/ManifestEditor.git


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.


================================================
FILE: README.md
================================================
# LSPatch Framework

[![Build](https://img.shields.io/github/actions/workflow/status/LSPosed/LSPatch/main.yml?branch=master&logo=github&label=Build&event=push)](https://github.com/LSPosed/LSPatch/actions/workflows/main.yml?query=event%3Apush+is%3Acompleted+branch%3Amaster) [![Crowdin](https://img.shields.io/badge/Localization-Crowdin-blueviolet?logo=Crowdin)](https://lsposed.crowdin.com/lspatch) [![Download](https://img.shields.io/github/v/release/LSPosed/LSPatch?color=orange&logoColor=orange&label=Download&logo=DocuSign)](https://github.com/LSPosed/LSPatch/releases/latest) [![Total](https://shields.io/github/downloads/LSPosed/LSPatch/total?logo=Bookmeter&label=Counts&logoColor=yellow&color=yellow)](https://github.com/LSPosed/LSPatch/releases)

## Introduction 

Rootless implementation of LSPosed framework, integrating Xposed API by inserting dex and so into the target APK.

## Supported Versions

- Min: Android 9
- Max: In theory, same with [LSPosed](https://github.com/LSPosed/LSPosed#supported-versions)

## Download

For stable releases, please go to [Github Releases page](https://github.com/LSPosed/LSPatch/releases)  
For canary build, please check [Github Actions](https://github.com/LSPosed/LSPatch/actions)  
Note: debug builds are only available in Github Actions  

## Usage

+ Through jar
1. Download `lspatch.jar`
1. Run `java -jar lspatch.jar`

+ Through manager
1. Download and install `manager.apk` on an Android device
1. Follow the instructions of the manager app

## Translation Contributing

You can contribute translation [here](https://lsposed.crowdin.com/lspatch).

## Credits

- [LSPosed](https://github.com/LSPosed/LSPosed): Core framework
- [Xpatch](https://github.com/WindySha/Xpatch): Fork source
- [Apkzlib](https://android.googlesource.com/platform/tools/apkzlib): Repacking tool

## License

LSPatch is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html).


================================================
FILE: apkzlib/.gitignore
================================================
/build


================================================
FILE: apkzlib/build.gradle.kts
================================================
val androidSourceCompatibility: JavaVersion by rootProject.extra
val androidTargetCompatibility: JavaVersion by rootProject.extra

plugins {
    id("java-library")
}

java {
    sourceCompatibility = androidSourceCompatibility
    targetCompatibility = androidTargetCompatibility
}

dependencies {
    implementation("com.google.code.findbugs:jsr305:3.0.2")
    implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
    implementation("org.bouncycastle:bcprov-jdk15on:1.70")
    api("com.google.guava:guava:32.0.1-jre")
    api("com.android.tools.build:apksig:8.0.2")
    compileOnlyApi("com.google.auto.value:auto-value-annotations:1.10.1")
    annotationProcessor("com.google.auto.value:auto-value:1.10.1")
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/AbstractCloseableByteSourceFromOutputStreamBuilder.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.base.Preconditions;
import java.io.IOException;

/**
 * Abstract implementation of a {@link CloseableByteSourceFromOutputStreamBuilder} that simplifies
 * the implementation of concrete instances. It implements the state machine implied by the
 * interface contract and requires subclasses to implement two methods:
 * {@link #doWrite(byte[], int, int)} -- that actually does writing and {@link #doBuild()} that
 * builds the {@link CloseableByteSource].
 */
abstract class AbstractCloseableByteSourceFromOutputStreamBuilder
    extends CloseableByteSourceFromOutputStreamBuilder {

  /**
   * Array that allows {@link #write(int)} to delegate to {@link #write(byte[], int, int)} without
   * having to create an array for each invocation.
   */
  private final byte[] tempByte;

  /**
   * Has the builder been closed? If it has, then {@link #build()} may be called, but none of the
   * writing methods can.
   */
  private boolean closed;

  /**
   * Has the builder been built? If this is {@code true} then {@link #closed} is also {@code true}.
   */
  private boolean built;

  /** Creates a new builder. */
  AbstractCloseableByteSourceFromOutputStreamBuilder() {
    tempByte = new byte[1];
    closed = false;
    built = false;
  }

  @Override
  public void write(byte[] b, int off, int len) throws IOException {
    Preconditions.checkState(!closed);
    doWrite(b, off, len);
  }

  @Override
  public void write(int b) throws IOException {
    tempByte[0] = (byte) b;
    write(tempByte, 0, 1);
  }

  @Override
  public void close() throws IOException {
    closed = true;
  }

  @Override
  public CloseableByteSource build() throws IOException {
    Preconditions.checkState(!built);
    closed = true;
    built = true;

    return doBuild();
  }

  /**
   * Same as {@link #write(byte[], int, int)}, but with the guarantee that the source has not been
   * built and the builder is still open.
   *
   * @param b see {@link #write(byte[], int, int)}
   * @param off see {@link #write(byte[], int, int)}
   * @param len see {@link #write(byte[], int, int)}
   * @throws IOException see {@link #write(byte[], int, int)}
   */
  protected abstract void doWrite(byte[] b, int off, int len) throws IOException;

  /**
   * Builds the {@link CloseableByteSource} from the written data. This method is at most invoked
   * once.
   *
   * @return the new source that will contain all data written to the builder so far
   * @throws IOException failed to create the byte source
   */
  protected abstract CloseableByteSource doBuild() throws IOException;
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ByteStorage.java
================================================
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.io.ByteSource;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;

/**
 * Interface for a storage that will temporarily save bytes. There are several factory methods to
 * create byte sources from several inputs, all of which may be discarded after the byte source has
 * been created. The data is saved in the storage and will be kept until the byte source is closed.
 */
public interface ByteStorage extends Closeable {
  /**
   * Creates a new byte source by fully reading an input stream.
   *
   * @param stream the input stream
   * @return a byte source containing the cached data from the given stream
   * @throws IOException failed to read the stream
   */
  CloseableByteSource fromStream(InputStream stream) throws IOException;

  /**
   * Creates a builder that is an output stream and can create a byte source.
   *
   * @return a builder where data can be written to and a {@link CloseableByteSource} can eventually
   *     be obtained from
   * @throws IOException failed to create the builder; this may happen if the builder require some
   *     preparation such as temporary storage allocation that may fail
   */
  CloseableByteSourceFromOutputStreamBuilder makeBuilder() throws IOException;

  /**
   * Creates a new byte source from another byte source.
   *
   * @param source the byte source to copy data from
   * @return the tracked byte source
   * @throws IOException failed to read data from the byte source
   */
  CloseableByteSource fromSource(ByteSource source) throws IOException;

  /**
   * Obtains the number of bytes currently used.
   *
   * @return the number of bytes
   */
  long getBytesUsed();

  /**
   * Obtains the maximum number of bytes ever used by this tracker.
   *
   * @return the number of bytes
   */
  long getMaxBytesUsed();
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ByteStorageFactory.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import java.io.IOException;

/** Factory that creates {@link ByteStorage}. */
public interface ByteStorageFactory {

  /**
   * Creates a new storage.
   *
   * @return a storage that should be closed when no longer used.
   */
  ByteStorage create() throws IOException;
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedByteStorage.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.ByteSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;

/**
 * Byte storage that breaks byte sources into smaller byte sources. This storage uses another
 * storage as a delegate and, when a source is requested, it will allocate one or more sources from
 * the delegate to build the requested source.
 */
public class ChunkBasedByteStorage implements ByteStorage {

  /** Size of the default chunk size. */
  private static final long DEFAULT_CHUNK_SIZE_BYTES = 10 * 1024 * 1024;

  /** Maximum size of each chunk. */
  private final long maxChunkSize;

  /** Byte storage where the data is actually stored. */
  private final ByteStorage delegate;

  /**
   * Creates a new storage breaking sources in chunks with the default maximum size and allocating
   * each chunk from {@code delegate}.
   */
  ChunkBasedByteStorage(ByteStorage delegate) {
    this(DEFAULT_CHUNK_SIZE_BYTES, delegate);
  }

  /**
   * Creates a new storage breaking sources in chunks with the maximum of {@code maxChunkSize} and
   * allocating each chunk from {@code delegate}.
   */
  ChunkBasedByteStorage(long maxChunkSize, ByteStorage delegate) {
    this.maxChunkSize = maxChunkSize;
    this.delegate = delegate;
  }

  /** Obtains the byte storage chunks are allocated from. */
  @VisibleForTesting // private otherwise.
  public ByteStorage getDelegate() {
    return delegate;
  }

  @Override
  public CloseableByteSource fromStream(InputStream stream) throws IOException {
    List<CloseableByteSource> sources = new ArrayList<>();
    while (true) {
      LimitedInputStream limitedInput = new LimitedInputStream(stream, maxChunkSize);
      sources.add(delegate.fromStream(limitedInput));
      if (limitedInput.isInputFinished()) {
        break;
      }
    }

    return new ChunkBasedCloseableByteSource(sources);
  }

  @Override
  public CloseableByteSourceFromOutputStreamBuilder makeBuilder() throws IOException {
    return new AbstractCloseableByteSourceFromOutputStreamBuilder() {
      private final List<CloseableByteSource> sources = new ArrayList<>();
      @Nullable private CloseableByteSourceFromOutputStreamBuilder currentBuilder = null;
      private long written = 0;

      @Override
      protected void doWrite(byte[] b, int off, int len) throws IOException {
        int actualOffset = off;
        int remaining = len;

        while (remaining > 0) {
          // Since we're writing data, make sure we have a builder to create the new source.
          if (currentBuilder == null) {
            currentBuilder = delegate.makeBuilder();
            written = 0;
          }

          // See how much we can write without exceeding maxChunkSize in the current builder.
          int maxWrite = (int) Math.min(maxChunkSize - written, remaining);
          currentBuilder.write(b, actualOffset, maxWrite);
          written += maxWrite;

          remaining -= maxWrite;
          actualOffset += maxWrite;

          // If we've reached the end of the chunk, create the source for the part we have and reset
          // to builder so we start a new one if there is more data.
          if (written == maxChunkSize) {
            sources.add(currentBuilder.build());
            currentBuilder = null;
          }
        }
      }

      @Override
      protected CloseableByteSource doBuild() throws IOException {
        // If we were writing a chunk, close it.
        if (currentBuilder != null) {
          sources.add(currentBuilder.build());
          currentBuilder = null;
        }

        return new ChunkBasedCloseableByteSource(sources);
      }
    };
  }

  @Override
  public CloseableByteSource fromSource(ByteSource source) throws IOException {
    List<CloseableByteSource> sources = new ArrayList<>();

    long end = source.size();
    long start = 0;
    while (start < end) {
      long chunkSize = Math.min(end - start, maxChunkSize);
      sources.add(delegate.fromSource(source.slice(start, chunkSize)));
      start += chunkSize;
    }

    return new ChunkBasedCloseableByteSource(sources);
  }

  @Override
  public long getBytesUsed() {
    return delegate.getBytesUsed();
  }

  @Override
  public long getMaxBytesUsed() {
    return delegate.getMaxBytesUsed();
  }

  @Override
  public void close() throws IOException {
    delegate.close();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedByteStorageFactory.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import java.io.IOException;
import javax.annotation.Nullable;

/**
 * {@link ByteStorageFactory} that creates {@link ByteStorage} instances that keep all data in
 * memory.
 */
public class ChunkBasedByteStorageFactory implements ByteStorageFactory {

  /** Factory to create the delegate storages. */
  private final ByteStorageFactory delegate;

  /** Maximum size for chunks, if any. */
  @Nullable private final Long maxChunkSize;

  /** Creates a new factory whose storages are created using delegates from the given factory. */
  public ChunkBasedByteStorageFactory(ByteStorageFactory delegate) {
    this(delegate, /*maxChunkSize=*/ null);
  }

  /**
   * Creates a new factory whose storages use the given maximum chunk size and are created using
   * delegates from the given factory.
   */
  public ChunkBasedByteStorageFactory(ByteStorageFactory delegate, @Nullable Long maxChunkSize) {
    this.delegate = delegate;
    this.maxChunkSize = maxChunkSize;
  }

  @Override
  public ByteStorage create() throws IOException {
    if (maxChunkSize == null) {
      return new ChunkBasedByteStorage(delegate.create());
    } else {
      return new ChunkBasedByteStorage(maxChunkSize, delegate.create());
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedCloseableByteSource.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.android.tools.build.apkzlib.zip.utils.CloseableDelegateByteSource;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteSource;
import com.google.common.io.Closer;
import java.io.IOException;
import java.util.List;

/**
 * Byte source that has its data spread over several chunks, each with its own {@link
 * CloseableByteSource}.
 */
class ChunkBasedCloseableByteSource extends CloseableDelegateByteSource {

  /** The sources for data of all the chunks, in order. */
  private final ImmutableList<CloseableByteSource> sources;

  /** Creates a new source from the given sources. */
  ChunkBasedCloseableByteSource(List<CloseableByteSource> sources) throws IOException {
    super(ByteSource.concat(sources), sumSizes(sources));
    this.sources = ImmutableList.copyOf(sources);
  }

  /** Computes the size of this source by summing the sizes of all sources. */
  private static long sumSizes(List<CloseableByteSource> sources) throws IOException {
    long sum = 0;
    for (CloseableByteSource source : sources) {
      sum += source.size();
    }

    return sum;
  }

  @Override
  protected synchronized void innerClose() throws IOException {
    try (Closer closer = Closer.create()) {
      for (CloseableByteSource source : sources) {
        closer.register(source);
      }
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/CloseableByteSourceFromOutputStreamBuilder.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Output stream that creates a {@link CloseableByteSource} from the data that was written to it.
 * Calling {@link #close} is optional as {@link #build()} will also close the output stream.
 */
public abstract class CloseableByteSourceFromOutputStreamBuilder extends OutputStream {

  /**
   * Creates the source from the data that has been written to the stream. No more data can be
   * written to the output stream after this method has been called.
   *
   * @return a source that will provide the data that was written to the stream before this method
   *     is invoked; where this data is stored is not specified by this interface
   * @throws IOException failed to build the byte source
   */
  public abstract CloseableByteSource build() throws IOException;
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/InMemoryByteStorage.java
================================================
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.android.tools.build.apkzlib.zip.utils.CloseableDelegateByteSource;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/** Keeps track of used bytes allowing gauging memory usage. */
public class InMemoryByteStorage implements ByteStorage {

  /** Number of bytes currently in use. */
  private long bytesUsed;

  /** Maximum number of bytes used. */
  private long maxBytesUsed;

  @Override
  public CloseableByteSource fromStream(InputStream stream) throws IOException {
    byte[] data = ByteStreams.toByteArray(stream);
    updateUsage(data.length);
    return new CloseableDelegateByteSource(ByteSource.wrap(data), data.length) {
      @Override
      public synchronized void innerClose() throws IOException {
        super.innerClose();
        updateUsage(-sizeNoException());
      }
    };
  }

  @Override
  public CloseableByteSourceFromOutputStreamBuilder makeBuilder() throws IOException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    return new AbstractCloseableByteSourceFromOutputStreamBuilder() {
      @Override
      protected void doWrite(byte[] b, int off, int len) throws IOException {
        output.write(b, off, len);
        updateUsage(len);
      }

      @Override
      protected CloseableByteSource doBuild() throws IOException {
        byte[] data = output.toByteArray();
        return new CloseableDelegateByteSource(ByteSource.wrap(data), data.length) {
          @Override
          protected synchronized void innerClose() throws IOException {
            super.innerClose();
            updateUsage(-data.length);
          }
        };
      }
    };
  }

  @Override
  public CloseableByteSource fromSource(ByteSource source) throws IOException {
    return fromStream(source.openStream());
  }

  /**
   * Updates the memory used by this tracker.
   *
   * @param delta the number of bytes to add or remove, if negative
   */
  private synchronized void updateUsage(long delta) {
    bytesUsed += delta;
    if (maxBytesUsed < bytesUsed) {
      maxBytesUsed = bytesUsed;
    }
  }

  @Override
  public synchronized long getBytesUsed() {
    return bytesUsed;
  }

  @Override
  public synchronized long getMaxBytesUsed() {
    return maxBytesUsed;
  }

  @Override
  public void close() throws IOException {
    // Nothing to do on close.
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/InMemoryByteStorageFactory.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import java.io.IOException;

/**
 * {@link ByteStorageFactory} that creates {@link ByteStorage} instances that keep all data in
 * memory.
 */
public class InMemoryByteStorageFactory implements ByteStorageFactory {

  @Override
  public ByteStorage create() throws IOException {
    return new InMemoryByteStorage();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LimitedInputStream.java
================================================
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.bytestorage;

import java.io.IOException;
import java.io.InputStream;

/**
 * Input stream that reads only a limited number of bytes from another input stream before reporting
 * EOF. When closed, this stream will not close the underlying stream.
 *
 * <p>If the underlying stream does not have enough data, this stream will read all available data
 * from the underlying stream.
 */
class LimitedInputStream extends InputStream {
  /** Where the data comes from. */
  private final InputStream input;

  /** How many bytes remain in this stream. */
  private long remaining;

  /** Has EOF been detected in {@link #input}? */
  private boolean eofDetected;

  /**
   * Creates a new input stream.
   *
   * @param input where to read data from
   * @param maximum the maximum number of bytes to read from {@code input}
   */
  LimitedInputStream(InputStream input, long maximum) {
    this.input = input;
    this.remaining = maximum;
    this.eofDetected = false;
  }

  @Override
  public int read() throws IOException {
    if (remaining == 0) {
      return -1;
    }

    int r = input.read();
    if (r >= 0) {
      remaining--;
    } else {
      eofDetected = true;
    }

    return r;
  }

  @Override
  public int read(byte[] whereTo, int offset, int length) throws IOException {
    if (remaining == 0) {
      return -1;
    }

    int toRead = (int) Math.min(remaining, length);
    int r = input.read(whereTo, offset, toRead);
    if (r >= 0) {
      remaining -= r;
    } else {
      eofDetected = true;
    }

    return r;
  }

  /** Returns {@code true} if EOF has been detected in the {@code input} stream. */
  boolean isInputFinished() {
    return eofDetected;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LruTrackedCloseableByteSource.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.io.InputStream;

/**
 * Byte source that, until switched, will keep itself in the LRU queue. The byte source will
 * automatically remove itself from the queue once closed or moved to disk (see {@link
 * #moveToDisk(ByteStorage)}. This source should not be switched explicitly or tracking will not
 * work.
 *
 * <p>The source will consider an access to be opening a stream. Every time a stream is open the
 * source will move itself to the top of the LRU list.
 */
class LruTrackedCloseableByteSource extends SwitchableDelegateCloseableByteSource {
  /** The tracker being used. */
  private final LruTracker<LruTrackedCloseableByteSource> tracker;

  /** Are we still tracking usage? */
  private boolean tracking;

  /** Has the byte source been closed? */
  private boolean closed;

  /** Creates a new byte source based on the given source and using the provided tracker. */
  LruTrackedCloseableByteSource(
      CloseableByteSource delegate, LruTracker<LruTrackedCloseableByteSource> tracker)
      throws IOException {
    super(delegate);
    this.tracker = tracker;
    tracker.track(this);
    tracking = true;
    closed = false;
  }

  @Override
  public synchronized InputStream openStream() throws IOException {
    Preconditions.checkState(!closed);
    if (tracking) {
      tracker.access(this);
    }

    return super.openStream();
  }

  @Override
  protected synchronized void innerClose() throws IOException {
    closed = true;

    untrack();
    super.innerClose();
  }

  /**
   * Marks this source as not being tracked any more. May be called multiple times (only the first
   * one will do anything).
   */
  private synchronized void untrack() {
    if (tracking) {
      tracking = false;
      tracker.untrack(this);
    }
  }

  /**
   * Moves the contents of this source to a storage. This will untrack the source and switch its
   * contents to a new delegate provided by {@code diskStorage}.
   */
  synchronized void move(ByteStorage diskStorage) throws IOException {
    if (closed) {
      return;
    }

    CloseableByteSource diskSource = diskStorage.fromSource(this);
    untrack();
    switchSource(diskSource);
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LruTracker.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.util.TreeSet;
import javax.annotation.Nullable;

/**
 * A tracker that keeps a list of the last-recently-used objects of type {@code T}. The tracker
 * doesn't define what LRU means, it has a method, {@link #access(Object)} that marks the object as
 * being accessed and moves it to the top of the queue.
 *
 * <p>This implementation is O(log(N)) on all operations.
 *
 * <p>Implementation note: we don't keep track of time. Instead we use a counter that is incremented
 * every time a new access is done or a new object is tracked. Because of this, each access time is
 * unique for each object (although it will change after each access).
 */
class LruTracker<T> {

  /** Maps each object to its unique access time and vice-versa. */
  private final BiMap<T, Integer> objectToAccessTime;

  /**
   * Ordered set of all object's access times. This set has the same contents as {@code
   * objectToAccessTime.value()}. It is sorted from the highest access time (newest) to the lowest
   * access time (oldest).
   */
  private final TreeSet<Integer> accessTimes;

  /** Next access time to use for tracking or accessing. */
  private int currentTime;

  /** Creates a new tracker without any objects. */
  LruTracker() {
    currentTime = 1;
    objectToAccessTime = HashBiMap.create();
    accessTimes = new TreeSet<>((i0, i1) -> i1 - i0);
  }

  /** Starts tracking an object. This object's will be the most recently used. */
  synchronized void track(T object) {
    Preconditions.checkState(!objectToAccessTime.containsKey(object));
    objectToAccessTime.put(object, currentTime);
    accessTimes.add(currentTime);
    currentTime++;
  }

  /** Stops tracking an object. */
  synchronized void untrack(T object) {
    Preconditions.checkState(objectToAccessTime.containsKey(object));
    accessTimes.remove(objectToAccessTime.get(object));
    objectToAccessTime.remove(object);
  }

  /** Marks the given object as having been accessed promoting it as the most recently used. */
  synchronized void access(T object) {
    untrack(object);
    track(object);
  }

  /**
   * Obtains the position of an object in the queue. It will be {@code 0} for the most recently used
   * object.
   */
  synchronized int positionOf(T object) {
    Preconditions.checkState(objectToAccessTime.containsKey(object));
    int lastAccess = objectToAccessTime.get(object);
    return accessTimes.headSet(lastAccess).size();
  }

  /**
   * Obtains the last element, the one last accessed earliest. Will return empty if there are no
   * objects being tracked.
   */
  @Nullable
  synchronized T last() {
    if (accessTimes.isEmpty()) {
      return null;
    }

    return objectToAccessTime.inverse().get(accessTimes.last());
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/OverflowToDiskByteStorage.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.ByteSource;
import java.io.IOException;
import java.io.InputStream;

/**
 * Byte storage that keeps data in memory up to a certain size. After that, older sources are moved
 * to disk and the newer ones served from memory.
 *
 * <p>Once unloaded to disk, sources are not reloaded into memory as that would be in direct
 * conflict with the filesystem's caching and the costs would probably outweight the benefits.
 *
 * <p>The maximum memory used by storage is actually larger than the maximum provided. It may exceed
 * the limit by the size of one source. That is because sources are always loaded into memory before
 * the storage decides to flush them to disk.
 */
public class OverflowToDiskByteStorage implements ByteStorage {

  /** Size of the default memory cache. */
  private static final long DEFAULT_MEMORY_CACHE_BYTES = 50 * 1024 * 1024;

  /** In-memory storage. */
  private final InMemoryByteStorage memoryStorage;

  /** Disk-based storage. */
  @VisibleForTesting // private otherwise.
  final TemporaryDirectoryStorage diskStorage;

  /** Tracker that keeps all memory sources. */
  private final LruTracker<LruTrackedCloseableByteSource> memorySourcesTracker;

  /** Maximum amount of data to keep in memory. */
  private final long memoryCacheSize;

  /** Maximum amount of data used. */
  private long maxBytesUsed;

  /**
   * Creates a new byte storage with the default memory cache using the provided temporary directory
   * to write data that overflows the memory size.
   *
   * @param temporaryDirectoryFactory the factory used to create a temporary directory where to
   *     overflow to; the created directory will be closed when the {@link
   *     OverflowToDiskByteStorage} object is closed
   * @throws IOException failed to create the temporary directory
   */
  public OverflowToDiskByteStorage(TemporaryDirectoryFactory temporaryDirectoryFactory)
      throws IOException {
    this(DEFAULT_MEMORY_CACHE_BYTES, temporaryDirectoryFactory);
  }

  /**
   * Creates a new byte storage with the given memory cache size using the provided temporary
   * directory to write data that overflows the memory size.
   *
   * @param memoryCacheSize the in-memory cache; a value of {@link 0} will effectively disable
   *     in-memory caching
   * @param temporaryDirectoryFactory the factory used to create a temporary directory where to
   *     overflow to; the created directory will be closed when the {@link
   *     OverflowToDiskByteStorage} object is closed
   * @throws IOException failed to create the temporary directory
   */
  public OverflowToDiskByteStorage(
      long memoryCacheSize, TemporaryDirectoryFactory temporaryDirectoryFactory)
      throws IOException {
    memoryStorage = new InMemoryByteStorage();
    diskStorage = new TemporaryDirectoryStorage(temporaryDirectoryFactory);
    this.memoryCacheSize = memoryCacheSize;
    this.memorySourcesTracker = new LruTracker<>();
  }

  @Override
  public CloseableByteSource fromStream(InputStream stream) throws IOException {
    CloseableByteSource memSource =
        new LruTrackedCloseableByteSource(memoryStorage.fromStream(stream), memorySourcesTracker);
    checkMaxUsage();
    reviewSources();
    return memSource;
  }

  @Override
  public CloseableByteSourceFromOutputStreamBuilder makeBuilder() throws IOException {
    CloseableByteSourceFromOutputStreamBuilder memBuilder = memoryStorage.makeBuilder();
    return new AbstractCloseableByteSourceFromOutputStreamBuilder() {
      @Override
      protected void doWrite(byte[] b, int off, int len) throws IOException {
        memBuilder.write(b, off, len);
      }

      @Override
      protected CloseableByteSource doBuild() throws IOException {
        CloseableByteSource memSource =
            new LruTrackedCloseableByteSource(memBuilder.build(), memorySourcesTracker);
        checkMaxUsage();
        reviewSources();
        return memSource;
      }
    };
  }

  @Override
  public CloseableByteSource fromSource(ByteSource source) throws IOException {
    CloseableByteSource memSource =
        new LruTrackedCloseableByteSource(memoryStorage.fromSource(source), memorySourcesTracker);
    checkMaxUsage();
    reviewSources();
    return memSource;
  }

  @Override
  public synchronized long getBytesUsed() {
    return memoryStorage.getBytesUsed() + diskStorage.getBytesUsed();
  }

  @Override
  public synchronized long getMaxBytesUsed() {
    return maxBytesUsed;
  }

  /** Checks if we have reached a new high of data usage and set it. */
  private synchronized void checkMaxUsage() {
    if (getBytesUsed() > maxBytesUsed) {
      maxBytesUsed = getBytesUsed();
    }
  }

  /** Checks if any of the sources needs to be written to disk or loaded into memory. */
  private synchronized void reviewSources() throws IOException {
    // Move data from memory to disk until we have at most memoryCacheSize bytes in memory.
    while (memoryStorage.getBytesUsed() > memoryCacheSize) {
      LruTrackedCloseableByteSource last = memorySourcesTracker.last();
      if (last != null) {
        LruTrackedCloseableByteSource lastSource = last;
        lastSource.move(diskStorage);
      }
    }
  }

  /** Obtains the number of bytes stored in memory. */
  public long getMemoryBytesUsed() {
    return memoryStorage.getBytesUsed();
  }

  /** Obtains the maximum number of bytes ever stored in memory. */
  public long getMaxMemoryBytesUsed() {
    return memoryStorage.getMaxBytesUsed();
  }

  /** Obtains the number of bytes stored in disk. */
  public long getDiskBytesUsed() {
    return diskStorage.getBytesUsed();
  }

  /** Obtains the maximum number of bytes ever stored in disk. */
  public long getMaxDiskBytesUsed() {
    return diskStorage.getMaxBytesUsed();
  }

  @Override
  public void close() throws IOException {
    memoryStorage.close();
    diskStorage.close();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/OverflowToDiskByteStorageFactory.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import java.io.IOException;
import javax.annotation.Nullable;

/**
 * {@link ByteStorageFactory} that creates instances of {@link ByteStorage} that will keep some data
 * in memory and will overflow to disk when necessary.
 */
public class OverflowToDiskByteStorageFactory implements ByteStorageFactory {

  /** How much data we want to keep in cache? If {@code null} then we want the default value. */
  @Nullable private final Long memoryCacheSizeInBytes;

  /** Factory that creates temporary directories. */
  private final TemporaryDirectoryFactory temporaryDirectoryFactory;

  /**
   * Creates a new factory with an optional in-memory size and a temporary directory for overflow.
   *
   * @param temporaryDirectoryFactory a factory that creates temporary directories that will be used
   *     for overflow of the {@link ByteStorage} instances created by this factory
   */
  public OverflowToDiskByteStorageFactory(TemporaryDirectoryFactory temporaryDirectoryFactory) {
    this(null, temporaryDirectoryFactory);
  }

  /**
   * Creates a new factory with an optional in-memory size and a temporary directory for overflow.
   *
   * @param memoryCacheSizeInBytes how many bytes to keep in memory? If {@code null} then a default
   *     value will be used
   * @param temporaryDirectoryFactory a factory that creates temporary directories that will be used
   *     for overflow of the {@link ByteStorage} instances created by this factory
   */
  public OverflowToDiskByteStorageFactory(
      Long memoryCacheSizeInBytes, TemporaryDirectoryFactory temporaryDirectoryFactory) {
    this.memoryCacheSizeInBytes = memoryCacheSizeInBytes;
    this.temporaryDirectoryFactory = temporaryDirectoryFactory;
  }

  @Override
  public ByteStorage create() throws IOException {
    if (memoryCacheSizeInBytes == null) {
      return new OverflowToDiskByteStorage(temporaryDirectoryFactory);
    } else {
      return new OverflowToDiskByteStorage(memoryCacheSizeInBytes, temporaryDirectoryFactory);
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/SwitchableDelegateCloseableByteSource.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.io.Closer;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Byte source that delegates to another byte source that can be switched dynamically.
 *
 * <p>This byte source encloses another byte source (the delegate) and allows switching the
 * delegate. Switching is done transparently for the user (as long as the new byte source represents
 * the same data) maintaining all open streams working, but now streaming from the new source.
 */
class SwitchableDelegateCloseableByteSource extends CloseableByteSource {

  /** The current delegate. */
  private CloseableByteSource delegate;

  /** Has the byte source been closed? */
  private boolean closed;

  /**
   * Streams that have been opened, but not yet closed. These are all the streams that have to be
   * switched when we switch delegates.
   */
  private final List<SwitchableDelegateInputStream> nonClosedStreams;

  /** Creates a new source using {@code source} as delegate. */
  SwitchableDelegateCloseableByteSource(CloseableByteSource source) {
    this.delegate = source;
    nonClosedStreams = new ArrayList<>();
  }

  @Override
  protected synchronized void innerClose() throws IOException {
    closed = true;

    try (Closer closer = Closer.create()) {
      for (SwitchableDelegateInputStream stream : nonClosedStreams) {
        closer.register(stream);
      }

      nonClosedStreams.clear();
    }

    delegate.close();
  }

  @Override
  public synchronized InputStream openStream() throws IOException {
    SwitchableDelegateInputStream stream =
        new SwitchableDelegateInputStream(delegate.openStream()) {
          // Can't have a lock on the stream while we synchronize the removal of nonClosedStreams
          // because it can deadlock when called in parallel with switchSource as the lock order is
          // reversed. The lack of synchronization is OK because we don't access any data on the
          // stream anyway until super.close() is called.
          @SuppressWarnings("UnsynchronizedOverridesSynchronized")
          @Override
          public void close() throws IOException {
            // Remove the stream on close.
            synchronized (SwitchableDelegateCloseableByteSource.this) {
              nonClosedStreams.remove(this);
            }

            super.close();
          }
        };

    nonClosedStreams.add(stream);
    return stream;
  }

  /**
   * Switches the current source for {@code source}. All streams are kept valid. The current source
   * is closed.
   *
   * <p>If the current source has already been closed, {@code source} will also be closed and
   * nothing else is done.
   *
   * <p>Otherwise, as long as it is possible to open enough input streams from {@code source} to
   * replace all current input streams, the source if changed. Any errors while closing input
   * streams (which happens during switching -- see {@link
   * SwitchableDelegateInputStream#switchStream(InputStream)}) or closing the old source are
   * reported as thrown {@code IOException}
   */
  synchronized void switchSource(CloseableByteSource source) throws IOException {
    if (source == delegate) {
      return;
    }

    if (closed) {
      source.close();
      return;
    }

    List<InputStream> switchStreams = new ArrayList<>();
    for (int i = 0; i < nonClosedStreams.size(); i++) {
      switchStreams.add(source.openStream());
    }

    CloseableByteSource oldDelegate = delegate;
    delegate = source;

    // A bit of trickery. We want to call switchStream for all streams. switchStream will
    // successfully switch the stream even if it throws an exception (if it does, it means it
    // failed to close the old stream). So we want to continue switching and recording all
    // exceptions. Closer() has that logic already so we register each stream switch as a close
    // operation.
    try (Closer closer = Closer.create()) {
      for (int i = 0; i < nonClosedStreams.size(); i++) {
        SwitchableDelegateInputStream nonClosedStream = nonClosedStreams.get(i);
        InputStream switchStream = switchStreams.get(i);
        closer.register(() -> nonClosedStream.switchStream(switchStream));
      }

      closer.register(oldDelegate);
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/SwitchableDelegateInputStream.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;

/**
 * Input stream that delegates to another input stream, but can switch transparently the source
 * input stream.
 *
 * <p>Given a set of input streams that return the same data, this input stream will read from one
 * and allow switching to read from other streams continuing from the offset that was initially
 * read. The result is only meaningful if all streams read the same data.
 *
 * <p>This class allows transparently to switch between different implementations of the underlying
 * streams (memory, disk, etc.) while transparently providing data to users. It does not support
 * marking and it is multi-thread safe.
 */
class SwitchableDelegateInputStream extends InputStream {

  /** The input stream that is currently providing data. */
  private InputStream delegate;

  /**
   * Current offset in the input stream. We keep track of this to allow skipping data when switching
   * input streams.
   */
  private long currentOffset;

  /** Have we reached the end of stream? */
  @VisibleForTesting // private otherwise.
  boolean endOfStreamReached;

  /**
   * If a switch has occurred, how many bytes still need to be skipped in the input stream to
   * continue reading from the same position?
   */
  private long needsSkipping;

  SwitchableDelegateInputStream(InputStream delegate) {
    this.delegate = delegate;
    currentOffset = 0;
    endOfStreamReached = false;
    needsSkipping = 0;
  }

  /**
   * Skips data in the input stream if it has been switched and there is data to skip. Will fail if
   * we can't skip all the data.
   */
  private void skipDataIfNeeded() throws IOException {
    while (needsSkipping > 0) {
      long skipped = delegate.skip(needsSkipping);
      if (skipped == 0) {
        throw new IOException("Skipping InputStream after switching failed");
      }

      needsSkipping -= skipped;
    }
  }

  /** Same as {@link #increaseOffset(long)}. */
  private int increaseOffset(int amount) {
    return (int) increaseOffset((long) amount);
  }

  /**
   * Increases the current offset after reading. {@code amount} will indicate how many bytes we have
   * read. It {@code -1} then we know we've reached the end of the stream and {@link
   * #endOfStreamReached} is set to {@code true}.
   */
  private long increaseOffset(long amount) {
    if (amount > 0) {
      currentOffset += amount;
    }

    if (amount == -1) {
      endOfStreamReached = true;
    }

    return amount;
  }

  @Override
  public synchronized int read(byte[] b) throws IOException {
    if (endOfStreamReached) {
      return -1;
    }

    skipDataIfNeeded();
    return increaseOffset(delegate.read(b));
  }

  @Override
  public synchronized int read(byte[] b, int off, int len) throws IOException {
    if (endOfStreamReached) {
      return -1;
    }

    skipDataIfNeeded();
    return increaseOffset(delegate.read(b, off, len));
  }

  @Override
  public synchronized int read() throws IOException {
    if (endOfStreamReached) {
      return -1;
    }

    skipDataIfNeeded();
    int r = delegate.read();
    if (r == -1) {
      endOfStreamReached = true;
    } else {
      increaseOffset(1);
    }

    return r;
  }

  @Override
  public synchronized long skip(long n) throws IOException {
    if (endOfStreamReached) {
      return 0;
    }

    skipDataIfNeeded();
    return increaseOffset(delegate.skip(n));
  }

  @Override
  public synchronized int available() throws IOException {
    if (endOfStreamReached) {
      return 0;
    }

    skipDataIfNeeded();
    return delegate.available();
  }

  @Override
  public synchronized void close() throws IOException {
    endOfStreamReached = true;
    delegate.close();
  }

  @Override
  public void mark(int readlimit) {
    // We don't support marking.
  }

  @Override
  public void reset() throws IOException {
    throw new IOException("Mark not supported");
  }

  @Override
  public boolean markSupported() {
    return false;
  }

  /**
   * Switches the stream used.
   *
   * <p>The stream that is currently in use and the new stream will be used in further operations.
   * If this stream has already reached the end, {@code newStream} will be closed immediately and no
   * other action is taken. If the stream has not reached the end, any exception reported is due to
   * closing the stream currently in use, the new stream is not affected and this stream can still
   * be used to read from {@code newStream}.
   */
  synchronized void switchStream(InputStream newStream) throws IOException {
    if (newStream == delegate) {
      return;
    }

    try (InputStream oldDelegate = delegate) {
      delegate = newStream;
      needsSkipping = currentOffset;
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectory.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.google.common.annotations.VisibleForTesting;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * A temporary directory is a directory that creates temporary files. Upon close, all temporary
 * files are removed. Whether the directory itself is removed is dependent on the actual
 * implementation.
 */
public interface TemporaryDirectory extends Closeable {

  /**
   * Creates a new file in the directory. This method returns a new file that deleted, recreated,
   * read and written freely by the caller. No assumptions are made on the contents of this file
   * except that it will be deleted it if it still exists when the temporary directory is closed.
   */
  File newFile() throws IOException;

  /** Obtains the directory, only useful for tests. */
  @VisibleForTesting // private otherwise.
  File getDirectory();

  /**
   * Creates a new temporary directory in the system's temporary directory. All files created will
   * be created in this directory. The directory will be deleted (as long as all the files in it)
   * when closed.
   */
  static TemporaryDirectory newSystemTemporaryDirectory() throws IOException {
    Path tempDir = Files.createTempDirectory("tempdir_");
    TemporaryFile tempDirFile = new TemporaryFile(tempDir.toFile());
    return new TemporaryDirectory() {
      @Override
      public File newFile() throws IOException {
        return Files.createTempFile(tempDir, "temp_", ".data").toFile();
      }

      @Override
      public File getDirectory() {
        return tempDir.toFile();
      }

      @Override
      public void close() throws IOException {
        tempDirFile.close();
      }
    };
  }

  /**
   * Creates a new temporary directory that uses a fixed directory.
   *
   * @param directory the directory that will be returned; this directory won't be deleted when the
   *     {@link TemporaryDirectory} objects are closed
   * @return a {@link TemporaryDirectory} that will create files in {@code directory}
   */
  static TemporaryDirectory fixed(File directory) {
    return new TemporaryDirectory() {
      @Override
      public File newFile() throws IOException {
        return Files.createTempFile(directory.toPath(), "temp_", ".data").toFile();
      }

      @Override
      public File getDirectory() {
        return directory;
      }

      @Override
      public void close() throws IOException {}
    };
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectoryFactory.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import java.io.File;
import java.io.IOException;

/**
 * Factory that creates temporary directories. {@link
 * TemporaryDirectory#newSystemTemporaryDirectory()} conforms to this interface.
 */
public interface TemporaryDirectoryFactory {

  /**
   * Creates a new temporary directory.
   *
   * @return the new temporary directory that should be closed when finished
   * @throws IOException failed to create the temporary directory
   */
  TemporaryDirectory make() throws IOException;

  /**
   * Obtains a factory that creates temporary directories using {@link
   * TemporaryDirectory#fixed(File)}.
   *
   * @param directory the directory where all temporary files will be created
   * @return a factory that creates instances of {@link TemporaryDirectory} that creates all files
   *     inside {@code directory}
   */
  static TemporaryDirectoryFactory fixed(File directory) {
    return () -> TemporaryDirectory.fixed(directory);
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectoryStorage.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableByteSource;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Byte storage that keeps all byte sources as files in a temporary directory. Each data stored is
 * stored as a new file. The file is deleted as soon as the byte source is closed.
 */
public class TemporaryDirectoryStorage implements ByteStorage {

  /** Temporary directory to use. */
  @VisibleForTesting // private otherwise.
  final TemporaryDirectory temporaryDirectory;

  /** Number of bytes currently used. */
  private long bytesUsed;

  /** Maximum number of bytes used. */
  private long maxBytesUsed;

  /**
   * Creates a new storage using the provided temporary directory.
   *
   * @param temporaryDirectoryFactory a factory used to create the directory to use for temporary
   *     files; this directory will be closed when the {@link TemporaryDirectoryStorage} is closed.
   * @throws IOException failed to create the temporary directory
   */
  public TemporaryDirectoryStorage(TemporaryDirectoryFactory temporaryDirectoryFactory)
      throws IOException {
    this.temporaryDirectory = temporaryDirectoryFactory.make();
  }

  @Override
  public CloseableByteSource fromStream(InputStream stream) throws IOException {
    File temporaryFile = temporaryDirectory.newFile();
    try (FileOutputStream output = new FileOutputStream(temporaryFile)) {
      ByteStreams.copy(stream, output);
    }

    long size = temporaryFile.length();
    incrementBytesUsed(size);
    return new TemporaryFileCloseableByteSource(temporaryFile, () -> incrementBytesUsed(-size));
  }

  @Override
  public CloseableByteSourceFromOutputStreamBuilder makeBuilder() throws IOException {
    File temporaryFile = temporaryDirectory.newFile();
    return new AbstractCloseableByteSourceFromOutputStreamBuilder() {
      private final FileOutputStream output = new FileOutputStream(temporaryFile);

      @Override
      protected void doWrite(byte[] b, int off, int len) throws IOException {
        output.write(b, off, len);
        incrementBytesUsed(len);
      }

      @Override
      protected CloseableByteSource doBuild() throws IOException {
        output.close();
        long size = temporaryFile.length();
        return new TemporaryFileCloseableByteSource(temporaryFile, () -> incrementBytesUsed(-size));
      }
    };
  }

  @Override
  public CloseableByteSource fromSource(ByteSource source) throws IOException {
    try (InputStream stream = source.openStream()) {
      return fromStream(stream);
    }
  }

  @Override
  public synchronized long getBytesUsed() {
    return bytesUsed;
  }

  @Override
  public synchronized long getMaxBytesUsed() {
    return maxBytesUsed;
  }

  /** Increments the byte counter by the given amount (decrements if {@code amount} is negative). */
  private synchronized void incrementBytesUsed(long amount) {
    bytesUsed += amount;
    if (bytesUsed > maxBytesUsed) {
      maxBytesUsed = bytesUsed;
    }
  }

  @Override
  public void close() throws IOException {
    temporaryDirectory.close();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryFile.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.google.common.base.Preconditions;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;

/**
 * A temporary file or directory. Wraps a file or directory and deletes it (recursively, if it is a
 * directory) when closed.
 */
public class TemporaryFile implements Closeable {

  /** Has the file or directory represented by {@link #file} been deleted? */
  private boolean deleted;

  /**
   * The file or directory that will be deleted on close. May no longer exist if {@link #deleted} is
   * {@code true}.
   */
  private final File file;

  /**
   * Creates a new wrapper around the given file. The file or directory {@code file} will be deleted
   * (recursively, if it is a directory) on close.
   */
  public TemporaryFile(File file) {
    deleted = false;
    this.file = file;
  }

  /** Obtains the file or directory this temporary file refers to. */
  public File getFile() {
    Preconditions.checkState(!deleted, "File already deleted");
    return file;
  }

  @Override
  public void close() throws IOException {
    if (deleted) {
      return;
    }

    deleted = true;

    deleteFile(file);
  }

  /** Deletes a file or directory if it exists. */
  private void deleteFile(File file) throws IOException {
    if (file.isDirectory()) {
      File[] contents = file.listFiles();
      if (contents != null) {
        for (File subFile : contents) {
          deleteFile(subFile);
        }
      }
    }

    if (file.exists() && !file.delete()) {
      throw new IOException("Failed to delete '" + file.getAbsolutePath() + "'");
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryFileCloseableByteSource.java
================================================
package com.android.tools.build.apkzlib.bytestorage;

import com.android.tools.build.apkzlib.zip.utils.CloseableDelegateByteSource;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;

/**
 * Closeable byte source that uses a temporary file to store its contents. The file is deleted when
 * the byte source is closed.
 */
class TemporaryFileCloseableByteSource extends CloseableDelegateByteSource {

  /** Temporary file backing the byte source. */
  private final TemporaryFile temporaryFile;

  /** Callback to notify when the byte source is closed. */
  private final Runnable closeCallback;

  /**
   * Creates a new byte source based on the given file. The provided callback is executed when the
   * source is deleted. There is no guarantee about which thread invokes the callback (it is the
   * thread that closes the source).
   */
  TemporaryFileCloseableByteSource(File file, Runnable closeCallback) {
    super(Files.asByteSource(file), file.length());
    temporaryFile = new TemporaryFile(file);
    this.closeCallback = closeCallback;
  }

  @Override
  protected synchronized void innerClose() throws IOException {
    super.innerClose();
    temporaryFile.close();
    closeCallback.run();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/DigestAlgorithm.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.sign;


/** Message digest algorithms. */
public enum DigestAlgorithm {
  /**
   * SHA-1 digest.
   *
   * <p>Android 2.3 (API Level 9) to 4.2 (API Level 17) (inclusive) do not support SHA-2 JAR
   * signatures.
   *
   * <p>Moreover, platforms prior to API Level 18, without the additional Digest-Algorithms
   * attribute, only support SHA or SHA1 algorithm names in .SF and MANIFEST.MF attributes.
   */
  SHA1("SHA1", "SHA-1"),

  /** SHA-256 digest. */
  SHA256("SHA-256", "SHA-256");

  /**
   * API level which supports {@link #SHA256} with {@link SignatureAlgorithm#RSA} and {@link
   * SignatureAlgorithm#ECDSA}.
   */
  public static final int API_SHA_256_RSA_AND_ECDSA = 18;

  /**
   * API level which supports {@link #SHA256} for all {@link SignatureAlgorithm}s.
   *
   * <p>Before that, SHA256 can only be used with RSA and ECDSA.
   */
  public static final int API_SHA_256_ALL_ALGORITHMS = 21;

  /** Name of algorithm for message digest. */
  public final String messageDigestName;

  /** Name of attribute in signature file with the manifest digest. */
  public final String manifestAttributeName;

  /** Name of attribute in entry (both manifest and signature file) with the entry's digest. */
  public final String entryAttributeName;

  /**
   * Creates a digest algorithm.
   *
   * @param attributeName attribute name in the signature file
   * @param messageDigestName name of algorithm for message digest
   */
  DigestAlgorithm(String attributeName, String messageDigestName) {
    this.messageDigestName = messageDigestName;
    this.entryAttributeName = attributeName + "-Digest";
    this.manifestAttributeName = attributeName + "-Digest-Manifest";
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/ManifestGenerationExtension.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.sign;

import com.android.tools.build.apkzlib.utils.CachedSupplier;
import com.android.tools.build.apkzlib.utils.IOExceptionRunnable;
import com.android.tools.build.apkzlib.utils.IOExceptionWrapper;
import com.android.tools.build.apkzlib.zfile.ManifestAttributes;
import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileExtension;
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import javax.annotation.Nullable;

/**
 * Extension to {@link ZFile} that will generate a manifest. The extension will register
 * automatically with the {@link ZFile}.
 *
 * <p>Creating this extension will ensure a manifest for the zip exists. This extension will
 * generate a manifest if one does not exist and will update an existing manifest, if one does
 * exist. The extension will also provide access to the manifest so that others may update the
 * manifest.
 *
 * <p>Apart from standard manifest elements, this extension does not handle any particular manifest
 * features such as signing or adding custom attributes. It simply generates a plain manifest and
 * provides infrastructure so that other extensions can add data in the manifest.
 *
 * <p>The manifest itself will only be written when the {@link ZFileExtension#beforeUpdate()}
 * notification is received, meaning all manifest manipulation is done in-memory.
 */
public class ManifestGenerationExtension {

  /** Name of META-INF directory. */
  private static final String META_INF_DIR = "META-INF";

  /** Name of the manifest file. */
  static final String MANIFEST_NAME = META_INF_DIR + "/MANIFEST.MF";

  /** Who should be reported as the manifest builder. */
  private final String builtBy;

  /** Who should be reported as the manifest creator. */
  private final String createdBy;

  /** The file this extension is attached to. {@code null} if not yet registered. */
  @Nullable private ZFile zFile;

  /** The zip file's manifest. */
  private final Manifest manifest;

  /**
   * Byte representation of the manifest. There is no guarantee that two writes of the java's {@code
   * Manifest} object will yield the same byte array (there is no guaranteed order of entries in the
   * manifest).
   *
   * <p>Because we need the byte representation of the manifest to be stable if there are no changes
   * to the manifest, we cannot rely on {@code Manifest} to generate the byte representation every
   * time we need the byte representation.
   *
   * <p>This cache will ensure that we will request one byte generation from the {@code Manifest}
   * and will cache it. All further requests of the manifest's byte representation will receive the
   * same byte array.
   */
  private final CachedSupplier<byte[]> manifestBytes;

  /**
   * Has the current manifest been changed and not yet flushed? If {@link #dirty} is {@code true},
   * then {@link #manifestBytes} should not be valid. This means that marking the manifest as dirty
   * should also invalidate {@link #manifestBytes}. To avoid breaking the invariant, instead of
   * setting {@link #dirty}, {@link #markDirty()} should be called.
   */
  private boolean dirty;

  /** The extension to register with the {@link ZFile}. {@code null} if not registered. */
  @Nullable private ZFileExtension extension;

  /**
   * Creates a new extension. This will not register the extension with the provided {@link ZFile}.
   * Until {@link #register(ZFile)} is invoked, this extension is not used.
   *
   * @param builtBy who built the manifest?
   * @param createdBy who created the manifest?
   */
  public ManifestGenerationExtension(String builtBy, String createdBy) {
    this.builtBy = builtBy;
    this.createdBy = createdBy;
    manifest = new Manifest();
    dirty = false;
    manifestBytes =
        new CachedSupplier<>(
            () -> {
              ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
              try {
                manifest.write(outBytes);
              } catch (IOException e) {
                throw new IOExceptionWrapper(e);
              }

              return outBytes.toByteArray();
            });
  }

  /**
   * Marks the manifest as being dirty, <i>i.e.</i>, its data has changed since it was last read
   * and/or written.
   */
  private void markDirty() {
    dirty = true;
    manifestBytes.reset();
  }

  /**
   * Registers the extension with the {@link ZFile} provided in the constructor.
   *
   * @param zFile the zip file to add the extension to
   * @throws IOException failed to analyze the zip
   */
  public void register(ZFile zFile) throws IOException {
    Preconditions.checkState(extension == null, "register() has already been invoked.");
    this.zFile = zFile;

    rebuildManifest();

    extension =
        new ZFileExtension() {
          @Nullable
          @Override
          public IOExceptionRunnable beforeUpdate() {
            return ManifestGenerationExtension.this::updateManifest;
          }
        };

    this.zFile.addZFileExtension(extension);
  }

  /** Rebuilds the zip file's manifest, if it needs changes. */
  private void rebuildManifest() throws IOException {
    Verify.verifyNotNull(zFile, "zFile == null");

    StoredEntry manifestEntry = zFile.get(MANIFEST_NAME);

    if (manifestEntry != null) {
      /*
       * Read the manifest entry in the zip file. Make sure we store these byte sequence
       * because writing the manifest may not generate the same byte sequence, which may
       * trigger an unnecessary re-sign of the jar.
       */
      manifest.clear();
      byte[] manifestBytes = manifestEntry.read();
      manifest.read(new ByteArrayInputStream(manifestBytes));
      this.manifestBytes.precomputed(manifestBytes);
    }

    Attributes mainAttributes = manifest.getMainAttributes();
    String currentVersion = mainAttributes.getValue(ManifestAttributes.MANIFEST_VERSION);
    if (currentVersion == null) {
      setMainAttribute(
          ManifestAttributes.MANIFEST_VERSION, ManifestAttributes.CURRENT_MANIFEST_VERSION);
    } else {
      if (!currentVersion.equals(ManifestAttributes.CURRENT_MANIFEST_VERSION)) {
        throw new IOException("Unsupported manifest version: " + currentVersion + ".");
      }
    }

    /*
     * We "blindly" override all other main attributes.
     */
    setMainAttribute(ManifestAttributes.BUILT_BY, builtBy);
    setMainAttribute(ManifestAttributes.CREATED_BY, createdBy);
  }

  /**
   * Sets the value of a main attribute.
   *
   * @param attribute the attribute
   * @param value the value
   */
  private void setMainAttribute(String attribute, String value) {
    Attributes mainAttributes = manifest.getMainAttributes();
    String current = mainAttributes.getValue(attribute);
    if (!value.equals(current)) {
      mainAttributes.putValue(attribute, value);
      markDirty();
    }
  }

  /**
   * Updates the manifest in the zip file, if it has been changed.
   *
   * @throws IOException failed to update the manifest
   */
  private void updateManifest() throws IOException {
    Verify.verifyNotNull(zFile, "zFile == null");

    if (!dirty) {
      return;
    }

    zFile.add(MANIFEST_NAME, new ByteArrayInputStream(manifestBytes.get()));
    dirty = false;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SignatureAlgorithm.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.sign;

import java.security.NoSuchAlgorithmException;

/** Signature algorithm. */
public enum SignatureAlgorithm {
  /** RSA algorithm. */
  RSA("RSA", 1, "withRSA"),

  /** ECDSA algorithm. */
  ECDSA("EC", 18, "withECDSA"),

  /** DSA algorithm. */
  DSA("DSA", 1, "withDSA");

  /** Name of the private key as reported by {@code PrivateKey}. */
  public final String keyAlgorithm;

  /** Minimum SDK version that allows this signature. */
  public final int minSdkVersion;

  /** Suffix appended to digest algorithm to obtain signature algorithm. */
  public final String signatureAlgorithmSuffix;

  /**
   * Creates a new signature algorithm.
   *
   * @param keyAlgorithm the name as reported by {@code PrivateKey}
   * @param minSdkVersion minimum SDK version that allows this signature
   * @param signatureAlgorithmSuffix suffix for signature name with used with a digest
   */
  SignatureAlgorithm(String keyAlgorithm, int minSdkVersion, String signatureAlgorithmSuffix) {
    this.keyAlgorithm = keyAlgorithm;
    this.minSdkVersion = minSdkVersion;
    this.signatureAlgorithmSuffix = signatureAlgorithmSuffix;
  }

  /**
   * Obtains the signature algorithm that corresponds to a private key name applicable to a SDK
   * version.
   *
   * @param keyAlgorithm the named referred in the {@code PrivateKey}
   * @param minSdkVersion minimum SDK version to run
   * @return the algorithm that has {@link #keyAlgorithm} equal to {@code keyAlgorithm}
   * @throws NoSuchAlgorithmException if no algorithm was found for the given private key; an
   *     algorithm was found but is not applicable to the given SDK version
   */
  public static SignatureAlgorithm fromKeyAlgorithm(String keyAlgorithm, int minSdkVersion)
      throws NoSuchAlgorithmException {
    for (SignatureAlgorithm alg : values()) {
      if (alg.keyAlgorithm.equalsIgnoreCase(keyAlgorithm)) {
        if (alg.minSdkVersion > minSdkVersion) {
          throw new NoSuchAlgorithmException(
              "Signatures with "
                  + keyAlgorithm
                  + " keys are not supported on minSdkVersion "
                  + minSdkVersion
                  + ". They are supported only for minSdkVersion >= "
                  + alg.minSdkVersion);
        }

        return alg;
      }
    }

    throw new NoSuchAlgorithmException("Signing with " + keyAlgorithm + " keys is not supported");
  }

  /**
   * Obtains the name of the signature algorithm when used with a digest algorithm.
   *
   * @param digestAlgorithm the digest algorithm to use
   * @return the name of the signature algorithm
   */
  public String signatureAlgorithmName(DigestAlgorithm digestAlgorithm) {
    return digestAlgorithm.messageDigestName.replace("-", "") + signatureAlgorithmSuffix;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SigningExtension.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.tools.build.apkzlib.sign;

import com.android.apksig.ApkSignerEngine;
import com.android.apksig.ApkVerifier;
import com.android.apksig.DefaultApkSignerEngine;
import com.android.apksig.apk.ApkFormatException;
import com.android.apksig.internal.apk.ApkSigningBlockUtils;
import com.android.apksig.util.DataSink;
import com.android.apksig.util.DataSource;
import com.android.apksig.util.DataSources;
import com.android.tools.build.apkzlib.utils.IOExceptionRunnable;
import com.android.tools.build.apkzlib.utils.SigningBlockUtils;
import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileExtension;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Bytes;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;

/**
 * {@link ZFile} extension which signs the APK.
 *
 * <p>This extension is capable of signing the APK using JAR signing (aka v1 scheme) and APK
 * Signature Scheme v2 (aka v2 scheme). Which schemes are actually used is specified by parameters
 * to this extension's constructor.
 */
public class SigningExtension {
  private static final int MAX_READ_CHUNK_SIZE = 65536;

  // IMPLEMENTATION NOTE: Most of the heavy lifting is performed by the ApkSignerEngine primitive
  // from apksig library. This class is an adapter between ZFile extension and ApkSignerEngine.
  // This class takes care of invoking the right methods on ApkSignerEngine in response to ZFile
  // extension events/callbacks.
  //
  // The main issue leading to additional complexity in this class is that the current build
  // pipeline does not reuse ApkSignerEngine instances (or ZFile extension instances for that
  // matter) for incremental builds. Thus:
  // * ZFile extension receives no events for JAR entries already in the APK whereas
  //   ApkSignerEngine needs to know about all JAR entries to be covered by signature. Thus, this
  //   class, during "beforeUpdate" ZFile event, notifies ApkSignerEngine about JAR entries
  //   already in the APK which ApkSignerEngine hasn't yet been told about -- these are the JAR
  //   entries which the incremental build session did not touch.
  // * The build pipeline expects the APK not to change if no JAR entry was added to it or removed
  //   from it whereas ApkSignerEngine produces no output only if it has already produced a signed
  //   APK and no changes have since been made to it. This class addresses this issue by checking
  //   in its "register" method whether the APK is correctly signed and, only if that's the case,
  //   doesn't modify the APK unless a JAR entry is added to it or removed from it after
  //   "register".

  /** APK signer which performs most of the heavy lifting. */
  private final ApkSignerEngine signer;

  /** Names of APK entries which have been processed by {@link #signer}. */
  private final Set<String> signerProcessedOutputEntryNames = new HashSet<>();

  /** Signing block Id for SDK dependency block. */
  static final int DEPENDENCY_INFO_BLOCK_ID = 0x504b4453;

  /** SDK dependencies of the APK */
  @Nullable private byte[] sdkDependencyData;

  /**
   * Cached contents of the most recently output APK Signing Block or {@code null} if the block
   * hasn't yet been output.
   */
  @Nullable private byte[] cachedApkSigningBlock;

  /**
   * {@code true} if signatures may need to be output, {@code false} if there's no need to output
   * signatures. This is used in an optimization where we don't modify the APK if it's already
   * signed and if no JAR entries have been added to or removed from the file.
   */
  private boolean dirty;

  /** The extension registered with the {@link ZFile}. {@code null} if not registered. */
  @Nullable private ZFileExtension extension;

  /** The file this extension is attached to. {@code null} if not yet registered. */
  @Nullable private ZFile zFile;

  /** A buffer used to read data from entries to feed to digests */
  private final Supplier<byte[]> digestBuffer =
      Suppliers.memoize(() -> new byte[MAX_READ_CHUNK_SIZE]);

  /** An object that has all necessary information to sign the zip file and verify its signature */
  private final SigningOptions options;

  public SigningExtension(SigningOptions opts) throws InvalidKeyException {
    DefaultApkSignerEngine.SignerConfig signerConfig =
        new DefaultApkSignerEngine.SignerConfig.Builder(
                "CERT", opts.getKey(), opts.getCertificates())
            .build();
    signer =
        new DefaultApkSignerEngine.Builder(ImmutableList.of(signerConfig), opts.getMinSdkVersion())
            .setOtherSignersSignaturesPreserved(false)
            .setV1SigningEnabled(opts.isV1SigningEnabled())
            .setV2SigningEnabled(opts.isV2SigningEnabled())
            .setV3SigningEnabled(false)
            .setCreatedBy("1.0 (Android)")
            .build();
    if (opts.getSdkDependencyData() != null) {
      sdkDependencyData = opts.getSdkDependencyData();
    }
    if (opts.getExecutor() != null) {
      signer.setExecutor(opts.getExecutor());
    }
    this.options = opts;
  }

  public void register(ZFile zFile) throws NoSuchAlgorithmException, IOException {
    Preconditions.checkState(extension == null, "register() already invoked");
    this.zFile = zFile;
    switch (options.getValidation()) {
      case ALWAYS_VALIDATE:
        dirty = !isCurrentSignatureAsRequested();
        break;
      case ASSUME_VALID:
        if (options.isV1SigningEnabled()) {
          Set<String> entryNames =
              ImmutableSet.copyOf(
                  Iterables.transform(
                      zFile.entries(), e -> e.getCentralDirectoryHeader().getName()));
          StoredEntry manifestEntry = zFile.get(ManifestGenerationExtension.MANIFEST_NAME);

          Preconditions.checkNotNull(
              manifestEntry,
              "No manifest found in apk for incremental build with enabled v1 signature");
          signerProcessedOutputEntryNames.addAll(
              this.signer.initWith(manifestEntry.read(), entryNames));
        }

        dirty = false;
        break;
      case ASSUME_INVALID:
        dirty = true;
        break;
    }
    extension =
        new ZFileExtension() {
          @Override
          public IOExceptionRunnable added(StoredEntry entry, @Nullable StoredEntry replaced) {
            return () -> onZipEntryOutput(entry);
          }

          @Override
          public IOExceptionRunnable removed(StoredEntry entry) {
            String entryName = entry.getCentralDirectoryHeader().getName();
            return () -> onZipEntryRemovedFromOutput(entryName);
          }

          @Override
          public IOExceptionRunnable beforeUpdate() throws IOException {
            return () -> onOutputZipReadyForUpdate();
          }

          @Override
          public void entriesWritten() throws IOException {
            onOutputZipEntriesWritten();
          }

          @Override
          public void closed() {
            onOutputClosed();
          }
        };
    this.zFile.addZFileExtension(extension);
  }

  /**
   * Returns {@code true} if the APK's signatures are as requested by parameters to this signing
   * extension.
   */
  private boolean isCurrentSignatureAsRequested() throws IOException, NoSuchAlgorithmException {
    ApkVerifier.Result result;
    try {
      result =
          new ApkVerifier.Builder(zFile.asDataSource())
              .setMinCheckedPlatformVersion(options.getMinSdkVersion())
              .build()
              .verify();
    } catch (ApkFormatException e) {
      // Malformed APK
      return false;
    }

    if (!result.isVerified()) {
      // Signature(s) did not verify
      return false;
    }

    if ((result.isVerifiedUsingV1Scheme() != options.isV1SigningEnabled())
        || (result.isVerifiedUsingV2Scheme() != options.isV2SigningEnabled())) {
      // APK isn't signed with exactly the schemes we want it to be signed
      return false;
    }

    List<X509Certificate> verifiedSignerCerts = result.getSignerCertificates();
    if (verifiedSignerCerts.size() != 1) {
      // APK is not signed by exactly one signer
      return false;
    }

    byte[] expectedEncodedCert;
    byte[] actualEncodedCert;
    try {
      expectedEncodedCert = options.getCertificates().get(0).getEncoded();
      actualEncodedCert = verifiedSignerCerts.get(0).getEncoded();
    } catch (CertificateEncodingException e) {
      // Failed to encode signing certificates
      return false;
    }

    if (!Arrays.equals(expectedEncodedCert, actualEncodedCert)) {
      // APK is signed by a wrong signer
      return false;
    }

    // APK is signed the way we want it to be signed
    return true;
  }

  private void onZipEntryOutput(StoredEntry entry) throws IOException {
    setDirty();
    String entryName = entry.getCentralDirectoryHeader().getName();
    // This event may arrive after the entry has already been deleted. In that case, we don't
    // report the addition of the entry to ApkSignerEngine.
    if (entry.isDeleted()) {
      return;
    }
    ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest = signer.outputJarEntry(entryName);
    signerProcessedOutputEntryNames.add(entryName);
    if (inspectEntryRequest != null) {
      try (InputStream inputStream = new BufferedInputStream(entry.open())) {
        copyStreamToDataSink(inputStream, inspectEntryRequest.getDataSink());
      }
      inspectEntryRequest.done();
    }
  }

  private void copyStreamToDataSink(InputStream inputStream, DataSink dataSink) throws IOException {
    int bytesRead;
    byte[] buffer = digestBuffer.get();
    while ((bytesRead = inputStream.read(buffer)) > 0) {
      dataSink.consume(buffer, 0, bytesRead);
    }
  }

  private void onZipEntryRemovedFromOutput(String entryName) {
    setDirty();
    signer.outputJarEntryRemoved(entryName);
    signerProcessedOutputEntryNames.remove(entryName);
  }

  private void onOutputZipReadyForUpdate() throws IOException {
    if (!dirty) {
      return;
    }

    // Notify signer engine about ZIP entries that have appeared in the output without the
    // engine knowing. Also identify ZIP entries which disappeared from the output without the
    // engine knowing.
    Set<String> unprocessedRemovedEntryNames = new HashSet<>(signerProcessedOutputEntryNames);
    for (StoredEntry entry : zFile.entries()) {
      String entryName = entry.getCentralDirectoryHeader().getName();
      unprocessedRemovedEntryNames.remove(entryName);
      if (!signerProcessedOutputEntryNames.contains(entryName)) {
        // Signer engine is not yet aware that this entry is in the output
        onZipEntryOutput(entry);
      }
    }

    // Notify signer engine about entries which disappeared from the output without the engine
    // knowing
    for (String entryName : unprocessedRemovedEntryNames) {
      onZipEntryRemovedFromOutput(entryName);
    }

    // Check whether we need to output additional JAR entries which comprise the v1 signature
    ApkSignerEngine.OutputJarSignatureRequest addV1SignatureRequest;
    try {
      addV1SignatureRequest = signer.outputJarEntries();
    } catch (Exception e) {
      throw new IOException("Failed to generate v1 signature", e);
    }
    if (addV1SignatureRequest == null) {
      return;
    }

    // We need to output additional JAR entries which comprise the v1 signature
    List<ApkSignerEngine.OutputJarSignatureRequest.JarEntry> v1SignatureEntries =
        new ArrayList<>(addV1SignatureRequest.getAdditionalJarEntries());

    // Reorder the JAR entries comprising the v1 signature so that MANIFEST.MF is the first
    // entry. This ensures that it cleanly overwrites the existing MANIFEST.MF output by
    // ManifestGenerationExtension.
    for (int i = 0; i < v1SignatureEntries.size(); i++) {
      ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry = v1SignatureEntries.get(i);
      String name = entry.getName();
      if (!ManifestGenerationExtension.MANIFEST_NAME.equals(name)) {
        continue;
      }
      if (i != 0) {
        v1SignatureEntries.remove(i);
        v1SignatureEntries.add(0, entry);
      }
      break;
    }

    // Output the JAR entries comprising the v1 signature
    for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry : v1SignatureEntries) {
      String name = entry.getName();
      byte[] data = entry.getData();
      zFile.add(name, new ByteArrayInputStream(data));
    }

    addV1SignatureRequest.done();
  }

  private void onOutputZipEntriesWritten() throws IOException {
    if (!dirty) {
      return;
    }

    // Check whether we should output an APK Signing Block which contains v2 signatures
    byte[] apkSigningBlock;
    byte[] centralDirBytes = zFile.getCentralDirectoryBytes();
    byte[] eocdBytes = zFile.getEocdBytes();
    ApkSignerEngine.OutputApkSigningBlockRequest2 addV2SignatureRequest;
    // This event may arrive a second time -- after we write out the APK Signing Block. Thus, we
    // cache the block to speed things up. The cached block is invalidated by any changes to the
    // file (as reported to this extension).
    if (cachedApkSigningBlock != null) {
      apkSigningBlock = cachedApkSigningBlock;
      addV2SignatureRequest = null;
    } else {
      DataSource centralDir = DataSources.asDataSource(ByteBuffer.wrap(centralDirBytes));
      DataSource eocd = DataSources.asDataSource(ByteBuffer.wrap(eocdBytes));
      long zipEntriesSizeBytes =
          zFile.getCentralDirectoryOffset() - zFile.getExtraDirectoryOffset();
      DataSource zipEntries = zFile.asDataSource(0, zipEntriesSizeBytes);
      try {
        addV2SignatureRequest = signer.outputZipSections2(zipEntries, centralDir, eocd);
      } catch (NoSuchAlgorithmException
          | InvalidKeyException
          | SignatureException
          | ApkFormatException
          | IOException e) {
        throw new IOException("Failed to generate v2 signature", e);
      }

      if (addV2SignatureRequest != null) {
        apkSigningBlock = addV2SignatureRequest.getApkSigningBlock();
        if (sdkDependencyData != null) {
          apkSigningBlock =
              SigningBlockUtils.addToSigningBlock(
                  apkSigningBlock, sdkDependencyData, DEPENDENCY_INFO_BLOCK_ID);
        }
        apkSigningBlock =
            Bytes.concat(
                new byte[addV2SignatureRequest.getPaddingSizeBeforeApkSigningBlock()],
                apkSigningBlock);
      } else {
        apkSigningBlock = new byte[0];
        if (sdkDependencyData != null) {
          apkSigningBlock =
              SigningBlockUtils.addToSigningBlock(
                  apkSigningBlock, sdkDependencyData, DEPENDENCY_INFO_BLOCK_ID);
          int paddingSize =
              ApkSigningBlockUtils.generateApkSigningBlockPadding(
                      zipEntries, /* apkSigningBlockPaddingSupported */ true)
                  .getSecond();
          apkSigningBlock = Bytes.concat(new byte[paddingSize], apkSigningBlock);
        }
      }
      cachedApkSigningBlock = apkSigningBlock;
    }

    // Insert the APK Signing Block into the output right before the ZIP Central Directory and
    // accordingly update the start offset of ZIP Central Directory in ZIP End of Central
    // Directory.
    zFile.directWrite(
        zFile.getCentralDirectoryOffset() - zFile.getExtraDirectoryOffset(), apkSigningBlock);
    zFile.setExtraDirectoryOffset(apkSigningBlock.length);

    if (addV2SignatureRequest != null) {
      addV2SignatureRequest.done();
    }
  }

  private void onOutputClosed() {
    if (!dirty) {
      return;
    }
    signer.outputDone();
    dirty = false;
  }

  private void setDirty() {
    dirty = true;
    cachedApkSigningBlock = null;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SigningOptions.java
================================================
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.sign;

import com.android.apksig.util.RunnablesExecutor;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/** A class that contains data to initialize SigningExtension. */
@AutoValue
public abstract class SigningOptions {

    /** An implementation of builder pattern to create a {@link SigningOptions} object. */
    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder setKey(@Nonnull PrivateKey key);
        public abstract Builder setCertificates(@Nonnull ImmutableList<X509Certificate> certs);
        public abstract Builder setCertificates(X509Certificate... certs);
        public abstract Builder setV1SigningEnabled(boolean enabled);
        public abstract Builder setV2SigningEnabled(boolean enabled);
        public abstract Builder setMinSdkVersion(int version);
        public abstract Builder setValidation(@Nonnull Validation validation);
        public abstract Builder setExecutor(@Nullable RunnablesExecutor executor);
        public abstract Builder setSdkDependencyData(@Nullable byte[] sdkDependencyData);

        abstract SigningOptions autoBuild();

        public SigningOptions build() {
            SigningOptions options = autoBuild();
            Preconditions.checkArgument(options.getMinSdkVersion() >= 0, "minSdkVersion < 0");
            Preconditions.checkArgument(
                    !options.getCertificates().isEmpty(),
                    "There should be at least one certificate in SigningOptions");
            return options;
        }
    }

    public static Builder builder() {
        return new AutoValue_SigningOptions.Builder()
                .setV1SigningEnabled(false)
                .setV2SigningEnabled(false)
                .setValidation(Validation.ALWAYS_VALIDATE);
    }

    /** {@link PrivateKey} used to sign the archive. */
    public abstract PrivateKey getKey();

    /**
     * A list of the {@link X509Certificate}s to embed in the signed APKs. The first
     * element of the list must be the certificate associated with the private key.
     */
    public abstract ImmutableList<X509Certificate> getCertificates();

    /** Shows whether signing with JAR Signature Scheme (aka v1 signing) is enabled. */
    public abstract boolean isV1SigningEnabled();

    /** Shows whether signing with APK Signature Scheme v2 (aka v2 signing) is enabled. */
    public abstract boolean isV2SigningEnabled();

    /** Minimum SDK version supported. */
    public abstract int getMinSdkVersion();

    /** Strategy of package signature validation */
    public abstract Validation getValidation();

    @Nullable
    public abstract RunnablesExecutor getExecutor();

  /** SDK dependencies of the APK */
  @SuppressWarnings("mutable")
  @Nullable
  public abstract byte[] getSdkDependencyData();

    public enum Validation {
        /** Always perform signature validation */
        ALWAYS_VALIDATE,
        /**
         * Assume the signature is valid without validation i.e. don't resign if no files changed
         */
        ASSUME_VALID,
        /** Assume the signature is invalid without validation i.e. unconditionally resign */
        ASSUME_INVALID,
    }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/package-info.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * The {@code sign} package provides extensions for the {@code zip} package that allow:
 *
 * <ul>
 *   <li>Adding a {@code MANIFEST.MF} file to a zip making a jar.
 *   <li>Signing a jar.
 *   <li>Fully signing a jar using v2 apk signature.
 * </ul>
 *
 * <p>Because the {@code zip} package is completely independent of the {@code sign} package, the
 * actual coordination between the two is complex. The {@code sign} package works by registering
 * extensions with the {@code zip} package. These extensions are notified in changes made in the zip
 * and will change the zip file itself.
 *
 * <p>The {@link com.android.apkzlib.sign.ManifestGenerationExtension} extension will ensure the zip
 * has a manifest file and is, therefore, a valid jar. The {@link
 * com.android.apkzlib.sign.SigningExtension} extension will ensure the jar is signed.
 *
 * <p>The extension mechanism used is the one provided in the {@code zip} package (see {@link
 * com.android.apkzlib.zip.ZFile} and {@link com.android.apkzlib.zip.ZFileExtension}. Building the
 * zip and then operating the extensions is not done sequentially, as we don't want to build a zip
 * and then sign it. We want to build a zip that is automatically signed. Extension are basically
 * observers that register on the zip and are notified when things happen in the zip. They will then
 * modify the zip accordingly.
 *
 * <p>The zip file notifies extensions in 4 critical moments: when a file is added or removed from
 * the zip, when the zip is about to be flushed to disk and when the zip's entries have been flushed
 * but the central directory not. At these moments, the extensions can act to update the zip in any
 * way they need.
 *
 * <p>To see how this works, consider the manifest generation extension: when the extension is
 * created, it checks the zip file to see if there is a manifest. If a manifest exists and does not
 * need updating, it does not change anything, otherwise it generates a new manifest for the zip
 * file. At this point, the extension could write the manifest to the zip, but we opted not to. It
 * would be irrelevant anyway as the zip will only be written when flushed.
 *
 * <p>Now, when the {@code ZFile} notifies the extension that it is about to start writing the zip
 * file, the manifest extension, if it has noted that the manifest needs to be rewritten, will --
 * before the {@code ZFile} actually writes anything -- modify the zip and add or replace the
 * existing manifest file. So, process-wise, the zip is written only once with the correct manifest.
 * The flow is as follows (if only the manifest generation extension was added to the {@code
 * ZFile}):
 *
 * <ol>
 *   <li>{@code ZFile.update()} is called.
 *   <li>{@code ZFile} calls {@code beforeUpdate()} for all {@code ZFileExtensions} registered, in
 *       this case, only the instance of the anonymous inner class generated in the {@code
 *       ManifestGenerationExtension} constructor is invoked.
 *   <li>{@code ManifestGenerationExtension.updateManifest()} is called.
 *   <li>If the manifest does not need to be updated, {@code updateManifest()} returns immediately.
 *   <li>If the manifest needs updating, {@code ZFile.add()} is invoked to add or replace the
 *       manifest.
 *   <li>{@code ManifestGenerationExtension.updateManifest()} returns.
 *   <li>{@code ZFile.update()} continues and writes the zip file, containing the manifest.
 *   <li>The zip is finally written with an updated manifest.
 * </ol>
 *
 * <p>To generate a signed apk, we need to add a second extension, the {@code SigningExtension}.
 * This extension will also register listeners with the {@code ZFile}.
 *
 * <p>In this case the flow would be (starting a bit earlier for clarity and assuming a package task
 * in the build process):
 *
 * <ol>
 *   <li>Package task creates a {@code ZFile} on the target apk (or non-existing file, if there is
 *       no target apk in the output directory).
 *   <li>Package task configures the {@code ZFile} with alignment rules.
 *   <li>Package task creates a {@code ManifestGenerationExtension}.
 *   <li>Package task registers the {@code ManifestGenerationExtension} with the {@code ZFile}.
 *   <li>The {@code ManifestGenerationExtension} looks at the {@code ZFile} to see if there is valid
 *       manifest. No changes are done to the {@code ZFile}.
 *   <li>Package task creates a {@code SigningExtension}.
 *   <li>Package task registers the {@code SigningExtension} with the {@code ZFile}.
 *   <li>The {@code SigningExtension} registers a {@code ZFileExtension} with the {@code ZFile} and
 *       look at the {@code ZFile} to see if there is a valid signature file.
 *   <li>If there are changes to the digital signature file needed, these are marked internally in
 *       the extension. If there are changes needed to the digests, the manifest is updated (by
 *       calling {@code ManifestGenerationExtension}.<br>
 *       <em>(note that this point, the apk file, if any existed, has not been touched, the manifest
 *       is only updated in memory and the digests of all files in the apk, if any, have been
 *       computed and stored in memory only; the digital signature of the {@code SF} file has not
 *       been computed.) </em>
 *   <li>The Package task now adds all files to the {@code ZFile}.
 *   <li>For each file that is added (*), {@code ZFile} calls the added {@code ZFileExtension.added}
 *       method of all registered extensions.
 *   <li>The {@code ManifestGenerationExtension} ignores added invocations.
 *   <li>The {@code SigningExtension} computes the digest for the added file and stores them in the
 *       manifest.<br>
 *       <em>(when all files are added to the apk, all digests are computed and the manifest is
 *       updated but only in memory; the apk file has not been touched; also note that {@code ZFile}
 *       has not actually written anything to disk at this point, all files added are kept in
 *       memory).</em>
 *   <li>Package task calls {@code ZFile.update()} to update the apk.
 *   <li>{@code ZFile} calls {@code before()} for all {@code ZFileExtensions} registered. This is
 *       done before anything is written. In this case both the {@code ManifestGenerationExtension}
 *       and {@code SigningExtension} are invoked.
 *   <li>The {@code ManifestGenerationExtension} will update the {@code ZFile} with the new
 *       manifest, unless nothing has changed, in which case it does nothing.
 *   <li>The {@code SigningExtension} will add the SF file (unless nothing has changed), will
 *       compute the digital signature of the SF file and write it to the {@code ZFile}.<br>
 *       <em>(note that the order by which the {@code ManifestGenerationExtension} and {@code
 *       SigningExtension} are called is non-deterministic; however, this is not a problem because
 *       the manifest is already computed by the {@code ManifestGenerationExtension} at this time
 *       and the {@code SigningExtension} will obtain the manifest data from the {@code
 *       ManifestGenerationExtension} and not from the {@code ZFile}; this means that the {@code SF}
 *       file may be added to the {@code ZFile} before the {@code MF} file, but that is
 *       irrelevant.)</em>
 *   <li>Once both extensions have finished doing the {@code beforeUpdate()} method, the {@code
 *       ZFile.update()} method continues.
 *   <li>{@code ZFile.update()} writes all changes and new entries to the zip file.
 *   <li>{@code ZFile.update()} calls {@code ZFileExtension.entriesWritten()} for all registered
 *       extensions. {@code SigningExtension} will kick in at this point, if v2 signature has
 *       changed.
 *   <li>{@code ZFile} writes the central directory and EOCD.
 *   <li>{@code ZFile.update()} returns control to the package task.
 *   <li>The package task finishes.
 * </ol>
 *
 * <em>(*) There is a number of optimizations if we're adding files from another {@code ZFile},
 * which is the case when we add the output of aapt to the apk. In particular, files from the aapt
 * are ignored if they are already in the apk (same name, same CRC32) and also files copied from the
 * aapt's output are not recompressed (the binary compressed data is directly copied to the
 * zip).</em>
 *
 * <p>If there are no changes to the {@code ZFile} made by the package task and the file's manifest
 * and v1 signatures are correct, neither the {@code ManifestGenerationExtension} nor the {@code
 * SigningExtension} will not do anything on the {@code beforeUpdate()} and the {@code ZFile} won't
 * even be open for writing.
 *
 * <p>This implementation provides perfect incremental updates.
 *
 * <p>Additionally, by adding/removing extensions we can configure what type of apk we want:
 *
 * <ul>
 *   <li>No SigningExtension => Aligned, unsigned apk.
 *   <li>SigningExtension => Aligned, signed apk.
 * </ul>
 *
 * So, by configuring which extensions to add, the package task can decide what type of apk we want.
 */
package com.android.apkzlib.sign;


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/ApkZLibPair.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

/** Pair implementation to use with the {@code apkzlib} library. */
public class ApkZLibPair<T1, T2> {

  /** First value. */
  public T1 v1;

  /** Second value. */
  public T2 v2;

  /**
   * Creates a new pair.
   *
   * @param v1 the first value
   * @param v2 the second value
   */
  public ApkZLibPair(T1 v1, T2 v2) {
    this.v1 = v1;
    this.v2 = v2;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/CachedFileContents.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import com.google.common.base.Objects;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nullable;

/**
 * A cache for file contents. The cache allows closing a file and saving in memory its contents (or
 * some related information). It can then be used to check if the contents are still valid at some
 * later time. Typical usage flow is:
 *
 * <p>
 *
 * <pre>{@code
 * Object fileRepresentation = // ...
 * File toWrite = // ...
 * // Write file contents and update in memory representation
 * CachedFileContents<Object> contents = new CachedFileContents<Object>(toWrite);
 * contents.closed(fileRepresentation);
 *
 * // Later, when data is needed:
 * if (contents.isValid()) {
 *     fileRepresentation = contents.getCache();
 * } else {
 *     // Re-read the file and recreate the file representation
 * }
 * }</pre>
 *
 * @param <T> the type of cached contents
 */
public class CachedFileContents<T> {

  /** The file. */
  private final File file;

  /** Time when last closed (time when {@link #closed(Object)} was invoked). */
  private long lastClosed;

  /** Size of the file when last closed. */
  private long size;

  /** Hash of the file when closed. {@code null} if hashing failed for some reason. */
  @Nullable private HashCode hash;

  /** Cached data associated with the file. */
  @Nullable private T cache;

  /**
   * Creates a new contents. When the file is written, {@link #closed(Object)} should be invoked to
   * set the cache.
   *
   * @param file the file
   */
  public CachedFileContents(File file) {
    this.file = file;
  }

  /**
   * Should be called when the file's contents are set and the file closed. This will save the cache
   * and register the file's timestamp to later detect if it has been modified.
   *
   * <p>This method can be called as many times as the file has been written.
   *
   * @param cache an optional cache to save
   */
  public void closed(@Nullable T cache) {
    this.cache = cache;
    lastClosed = file.lastModified();
    size = file.length();
    hash = hashFile();
  }

  /**
   * Are the cached contents still valid? If this method determines that the file has been modified
   * since the last time {@link #closed(Object)} was invoked.
   *
   * @return are the cached contents still valid? If this method returns {@code false}, the cache is
   *     cleared
   */
  public boolean isValid() {
    boolean valid = true;

    if (!file.exists()) {
      valid = false;
    }

    if (valid && file.lastModified() != lastClosed) {
      valid = false;
    }

    if (valid && file.length() != size) {
      valid = false;
    }

    if (valid && !Objects.equal(hash, hashFile())) {
      valid = false;
    }

    if (!valid) {
      cache = null;
    }

    return valid;
  }

  /**
   * Obtains the cached data set with {@link #closed(Object)} if the file has not been modified
   * since {@link #closed(Object)} was invoked.
   *
   * @return the last cached data or {@code null} if the file has been modified since {@link
   *     #closed(Object)} has been invoked
   */
  @Nullable
  public T getCache() {
    return cache;
  }

  /**
   * Computes the hashcode of the cached file.
   *
   * @return the hash code
   */
  @Nullable
  private HashCode hashFile() {
    try {
      return Files.asByteSource(file).hash(Hashing.crc32());
    } catch (IOException e) {
      return null;
    }
  }

  /**
   * Obtains the file used for caching.
   *
   * @return the file; this file always exists and contains the old (cached) contents of the file
   */
  public File getFile() {
    return file;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/CachedSupplier.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import com.google.common.base.Supplier;

/**
 * Supplier that will cache a computed value and always supply the same value. It can be used to
 * lazily compute data. For example:
 *
 * <pre>{@code
 * CachedSupplier<Integer> value = new CachedSupplier<>(() -> {
 *     Integer result;
 *     // Do some expensive computation.
 *     return result;
 * });
 *
 * if (a) {
 *     // We need the result of the expensive computation.
 *     Integer r = value.get();
 * }
 *
 * if (b) {
 *     // We also need the result of the expensive computation.
 *     Integer r = value.get();
 * }
 *
 * // If neither a nor b are true, we avoid doing the computation at all.
 * }</pre>
 */
public class CachedSupplier<T> {

  /**
   * The cached data, {@code null} if computation resulted in {@code null}. It is also {@code null}
   * if the cached data has not yet been computed.
   */
  private T cached;

  /** Is the current data in {@link #cached} valid? */
  private boolean valid;

  /** Actual supplier of data, if computation is needed. */
  private final Supplier<T> supplier;

  /** Creates a new supplier. */
  public CachedSupplier(Supplier<T> supplier) {
    valid = false;
    this.supplier = supplier;
  }

  /**
   * Obtains the value.
   *
   * @return the value, either cached (if one exists) or computed
   */
  public synchronized T get() {
    if (!valid) {
      cached = supplier.get();
      valid = true;
    }

    return cached;
  }

  /**
   * Resets the cache forcing a {@code get()} on the supplier next time {@link #get()} is invoked.
   */
  public synchronized void reset() {
    cached = null;
    valid = false;
  }

  /**
   * In some cases, we may be able to precompute the cache value (or load it from somewhere we had
   * previously stored it). This method allows the cache value to be loaded.
   *
   * <p>If this method is invoked, then an invocation of {@link #get()} will not trigger an
   * invocation of the supplier provided in the constructor.
   *
   * @param t the new cache contents; will replace any currently cache content, if one exists
   */
  public synchronized void precomputed(T t) {
    cached = t;
    valid = true;
  }

  /**
   * Checks if the contents of the cache are valid.
   *
   * @return are there valid contents in the cache?
   */
  public synchronized boolean isValid() {
    return valid;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionConsumer.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import java.io.IOException;
import javax.annotation.Nullable;

/** Consumer that can throw an {@link IOException}. */
public interface IOExceptionConsumer<T> {

  /**
   * Performs an operation on the given input.
   *
   * @param input the input
   */
  void accept(@Nullable T input) throws IOException;
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionFunction.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import com.google.common.base.Function;
import java.io.IOException;
import javax.annotation.Nullable;

/** Function that can throw an I/O Exception */
public interface IOExceptionFunction<F, T> {

  /**
   * Applies the function to the given input.
   *
   * @param input the input
   * @return the function result
   */
  @Nullable
  T apply(@Nullable F input) throws IOException;

  /**
   * Wraps a function that may throw an IO Exception throwing an {@link IOExceptionWrapper}.
   *
   * @param f the function
   */
  static <F, T> Function<F, T> asFunction(IOExceptionFunction<F, T> f) {
    return i -> {
      try {
        return f.apply(i);
      } catch (IOException e) {
        throw new IOExceptionWrapper(e);
      }
    };
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionRunnable.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import java.io.IOException;

/** Runnable that can throw I/O exceptions. */
public interface IOExceptionRunnable {

  /**
   * Runs the runnable.
   *
   * @throws IOException failed to run
   */
  void run() throws IOException;

  /**
   * Wraps a runnable that may throw an IO Exception throwing an {@code UncheckedIOException}.
   *
   * @param r the runnable
   */
  static Runnable asRunnable(IOExceptionRunnable r) {
    return () -> {
      try {
        r.run();
      } catch (IOException e) {
        throw new IOExceptionWrapper(e);
      }
    };
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionWrapper.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import java.io.IOException;

/**
 * Runtime exception used to encapsulate an IO Exception. This is used to allow throwing I/O
 * exceptions in functional interfaces that do not allow it and catching the exception afterwards.
 */
public class IOExceptionWrapper extends RuntimeException {

  /**
   * Creates a new exception.
   *
   * @param e the I/O exception to encapsulate
   */
  public IOExceptionWrapper(IOException e) {
    super(e);
  }

  @Override
  public IOException getCause() {
    return (IOException) super.getCause();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/SigningBlockUtils.java
================================================
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.utils;

import static java.nio.ByteOrder.LITTLE_ENDIAN;

import com.android.apksig.apk.ApkSigningBlockNotFoundException;
import com.android.apksig.apk.ApkUtils;
import com.android.apksig.apk.ApkUtils.ApkSigningBlock;
import com.android.apksig.internal.apk.ApkSigningBlockUtils;
import com.android.apksig.internal.util.Pair;
import com.android.apksig.util.DataSource;
import com.android.apksig.util.DataSources;
import com.android.apksig.zip.ZipFormatException;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.annotation.Nullable;

/** Generates and appends a new block to APK v2 Signature block. */
public final class SigningBlockUtils {

  private static final int MAGIC_NUM_BYTES = 16;
  private static final int BLOCK_LENGTH_NUM_BYTES = 8;
  static final int SIZE_OF_BLOCK_NUM_BYTES = 8;
  static final int BLOCK_ID_NUM_BYTES = 4;

  static final int ANDROID_COMMON_PAGE_ALIGNMENT_NUM_BYTES = 4096;
  static final int VERITY_PADDING_BLOCK_ID = 0x42726577;

  /**
   * Generates a new block with the given block value and block id, and appends it to the signing
   * block.
   *
   * @param signingBlock Block containing v2 signature and (optionally) padding block or null.
   * @param blockValue byte array containing block value of the new block or null.
   * @param blockId block id of the new block.
   * @return APK v2 block with signatures and the new block. If {@code blockValue} is null the
   *     {@code signingBlock} is returned without any modification. If {@code signingBlock} is null,
   *     a new signature block is created containing the new block and, optionally, padding block.
   */
  public static byte[] addToSigningBlock(byte[] signingBlock, byte[] blockValue, int blockId)
      throws IOException {
    if (blockValue == null || blockValue.length == 0) {
      return signingBlock;
    }
    if (signingBlock == null || signingBlock.length == 0) {
      return createSigningBlock(blockValue, blockId);
    }
    return appendToSigningBlock(signingBlock, blockValue, blockId);
  }

  /**
   * Adds a new block to the signature block and a padding block, if required.
   *
   * @param signingBlock APK v2 signing block containing : length prefix, signers (can include
   *     padding block), length postfix and APK sig v2 block magic.
   * @param blockValue byte array containing block value of the new block.
   * @param blockId block id of the new block.
   * @return APK v2 signing block containing : length prefix, signers including the new block (may
   *     include padding block as well), length postfix and APK sig v2 block magic.
   */
  private static byte[] appendToSigningBlock(byte[] signingBlock, byte[] blockValue, int blockId)
      throws IOException {
    ImmutableList<Pair<byte[], Integer>> entries =
        ImmutableList.<Pair<byte[], Integer>>builder()
            .addAll(extractAllSigners(DataSources.asDataSource(ByteBuffer.wrap(signingBlock))))
            .add(Pair.of(blockValue, blockId))
            .build();
    return ApkSigningBlockUtils.generateApkSigningBlock(entries);
  }

  /**
   * Generate APK sig v2 block containing a block composed of the provided block value and id, and
   * (optionally) padding block.
   */
  private static byte[] createSigningBlock(byte[] blockValue, int blockId) {
    return ApkSigningBlockUtils.generateApkSigningBlock(
        ImmutableList.of(Pair.of(blockValue, blockId)));
  }

  /**
   * Extracts all signing block entries except padding block.
   *
   * @param signingBlock APK v2 signing block containing: length prefix, signers (can include
   *     padding block), length postfix and APK sig v2 block magic.
   * @return list of block entry value and block entry id pairs.
   */
  private static ImmutableList<Pair<byte[], Integer>> extractAllSigners(DataSource signingBlock)
      throws IOException {
    long wholeBlockSize = signingBlock.size();
    // Take the segment of the existing signing block without the length prefix (8 bytes)
    // at the beginning and the length and magic (24 bytes) at the end, so it is just the sequence
    // of length prefix id value pairs.
    DataSource lengthPrefixedIdValuePairsSource =
        signingBlock.slice(
            SIZE_OF_BLOCK_NUM_BYTES,
            wholeBlockSize - 2 * SIZE_OF_BLOCK_NUM_BYTES - MAGIC_NUM_BYTES);
    final int lengthAndIdByteCount = BLOCK_LENGTH_NUM_BYTES + BLOCK_ID_NUM_BYTES;
    ByteBuffer lengthAndId = ByteBuffer.allocate(lengthAndIdByteCount).order(LITTLE_ENDIAN);
    ImmutableList.Builder<Pair<byte[], Integer>> idValuePairs = ImmutableList.builder();

    for (int index = 0; index <= lengthPrefixedIdValuePairsSource.size() - lengthAndIdByteCount; ) {
      lengthPrefixedIdValuePairsSource.copyTo(index, lengthAndIdByteCount, lengthAndId);
      lengthAndId.flip();
      int blockLength = Ints.checkedCast(lengthAndId.getLong());
      int id = lengthAndId.getInt();
      lengthAndId.clear();

      if (id != VERITY_PADDING_BLOCK_ID) {
        int blockValueSize = blockLength - BLOCK_ID_NUM_BYTES;
        ByteBuffer blockValue = ByteBuffer.allocate(blockValueSize);
        lengthPrefixedIdValuePairsSource.copyTo(
            index + BLOCK_LENGTH_NUM_BYTES + BLOCK_ID_NUM_BYTES, blockValueSize, blockValue);
        idValuePairs.add(Pair.of(blockValue.array(), id));
      }

      index += blockLength + BLOCK_LENGTH_NUM_BYTES;
    }
    return idValuePairs.build();
  }

  /**
   * Extract a block with the given id from the APK. If there is more than one block with the same
   * ID, the first block will be returned. If there are no block with the give id, {@code null} will
   * be returned.
   *
   * @param apk APK file
   * @param blockId id of the block to be extracted.
   */
  @Nullable
  public static ByteBuffer extractBlock(File apk, int blockId)
      throws IOException, ZipFormatException, ApkSigningBlockNotFoundException {
    try (RandomAccessFile file = new RandomAccessFile(apk, "r")) {
      DataSource apkDataSource = DataSources.asDataSource(file);
      ApkSigningBlock signingBlockInfo =
          ApkUtils.findApkSigningBlock(apkDataSource, ApkUtils.findZipSections(apkDataSource));

      DataSource wholeV2Block = signingBlockInfo.getContents();
      final int lengthAndIdByteCount = BLOCK_LENGTH_NUM_BYTES + BLOCK_ID_NUM_BYTES;
      DataSource signingBlock =
          wholeV2Block.slice(
              SIZE_OF_BLOCK_NUM_BYTES,
              wholeV2Block.size() - SIZE_OF_BLOCK_NUM_BYTES - MAGIC_NUM_BYTES);
      ByteBuffer lengthAndId =
          ByteBuffer.allocate(lengthAndIdByteCount).order(ByteOrder.LITTLE_ENDIAN);
      for (int index = 0; index <= signingBlock.size() - lengthAndIdByteCount; ) {
        signingBlock.copyTo(index, lengthAndIdByteCount, lengthAndId);
        lengthAndId.flip();
        int blockLength = (int) lengthAndId.getLong();
        int id = lengthAndId.getInt();
        lengthAndId.flip();
        if (id == blockId) {
          ByteBuffer block = ByteBuffer.allocate(blockLength - BLOCK_ID_NUM_BYTES);
          signingBlock.copyTo(
              index + lengthAndIdByteCount, blockLength - BLOCK_ID_NUM_BYTES, block);
          block.flip();
          return block;
        }
        index += blockLength + BLOCK_LENGTH_NUM_BYTES;
      }
      return null;
    }
  }

  private SigningBlockUtils() {}
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/package-info.java
================================================
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** Utilities to work with {@code apkzlib}. */
package com.android.tools.build.apkzlib.utils;


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkCreator.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nullable;

/** Creates or updates APKs based on provided entries. */
public interface ApkCreator extends Closeable {

  /**
   * Copies the content of a Jar/Zip archive into the receiver archive.
   *
   * <p>An optional predicate allows to selectively choose which files to copy over and an option
   * function allows renaming the files as they are copied.
   *
   * @param zip the zip to copy data from
   * @param transform an optional transform to apply to file names before copying them
   * @param isIgnored an optional filter or {@code null} to mark which out files should not be
   *     added, even through they are on the zip; if {@code transform} is specified, then this
   *     predicate applies after transformation
   * @throws IOException I/O error
   */
  void writeZip(
      File zip, @Nullable Function<String, String> transform, @Nullable Predicate<String> isIgnored)
      throws IOException;

  /**
   * Writes a new {@link File} into the archive. If a file already existed with the given path, it
   * should be replaced.
   *
   * @param inputFile the {@link File} to write.
   * @param apkPath the filepath inside the archive.
   * @throws IOException I/O error
   */
  void writeFile(File inputFile, String apkPath) throws IOException;

  /**
   * Deletes a file in a given path.
   *
   * @param apkPath the path to remove
   * @throws IOException failed to remove the entry
   */
  void deleteFile(String apkPath) throws IOException;

  /** Returns true if the APK will be rewritten on close. */
  boolean hasPendingChangesWithWait() throws IOException;
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkCreatorFactory.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

import com.android.tools.build.apkzlib.sign.SigningOptions;
import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import java.io.File;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/** Factory that creates instances of {@link ApkCreator}. */
public interface ApkCreatorFactory {

  /**
   * Creates an {@link ApkCreator} with a given output location, and signing information.
   *
   * @param creationData the information to create the APK
   */
  ApkCreator make(CreationData creationData);

  /**
   * Data structure with the required information to initiate the creation of an APK. See {@link
   * ApkCreatorFactory#make(CreationData)}.
   */
  @AutoValue
  abstract class CreationData {

    /** An implementation of builder pattern to create a {@link CreationData} object. */
    @AutoValue.Builder
    public abstract static class Builder {
      public abstract Builder setApkPath(@Nonnull File apkPath);

      public abstract Builder setSigningOptions(@Nonnull SigningOptions signingOptions);

      public abstract Builder setBuiltBy(@Nullable String buildBy);

      public abstract Builder setCreatedBy(@Nullable String createdBy);

      public abstract Builder setNativeLibrariesPackagingMode(
          NativeLibrariesPackagingMode packagingMode);

      public abstract Builder setNoCompressPredicate(Predicate<String> predicate);

      public abstract Builder setIncremental(boolean incremental);

      abstract CreationData autoBuild();

      public CreationData build() {
        CreationData data = autoBuild();
        Preconditions.checkArgument(data.getApkPath() != null, "Output apk path is not set");
        return data;
      }
    }

    public static Builder builder() {
      return new AutoValue_ApkCreatorFactory_CreationData.Builder()
          .setBuiltBy(null)
          .setCreatedBy(null)
          .setNoCompressPredicate(s -> false)
          .setIncremental(false);
    }

    /**
     * Obtains the path where the APK should be located. If the path already exists, then the APK
     * may be updated instead of re-created.
     *
     * @return the path that may already exist or not
     */
    public abstract File getApkPath();

    /**
     * Obtains the data used to sign the APK.
     *
     * @return the SigningOptions
     */
    @Nonnull
    public abstract Optional<SigningOptions> getSigningOptions();

    /**
     * Obtains the "built-by" text for the APK.
     *
     * @return the text or {@code null} if the default should be used
     */
    @Nullable
    public abstract String getBuiltBy();

    /**
     * Obtains the "created-by" text for the APK.
     *
     * @return the text or {@code null} if the default should be used
     */
    @Nullable
    public abstract String getCreatedBy();

    /** Returns the packaging policy that the {@link ApkCreator} should use for native libraries. */
    public abstract NativeLibrariesPackagingMode getNativeLibrariesPackagingMode();

    /** Returns the predicate to decide which file paths should be uncompressed. */
    public abstract Predicate<String> getNoCompressPredicate();

    /**
     * Returns if this apk build is incremental.
     *
     * As mentioned in {@link getApkPath} description, we may already have an existing apk in place.
     * This is the case when e.g. building APK via build system and this is not the first build.
     * In that case the build is called incremental and internal APK data might be reused speeding
     * the build up.
     */
    public abstract boolean isIncremental();
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkZFileCreator.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

import com.android.tools.build.apkzlib.zip.AlignmentRule;
import com.android.tools.build.apkzlib.zip.AlignmentRules;
import com.android.tools.build.apkzlib.zip.StoredEntry;
import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileOptions;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.io.Closer;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;

/** {@link ApkCreator} that uses {@link ZFileOptions} to generate the APK. */
class ApkZFileCreator implements ApkCreator {

  /** Suffix for native libraries. */
  private static final String NATIVE_LIBRARIES_SUFFIX = ".so";

  /** Shared libraries are alignment at 4096 boundaries. */
  private static final AlignmentRule SO_RULE =
      AlignmentRules.constantForSuffix(NATIVE_LIBRARIES_SUFFIX, 4096);

  /** The zip file. */
  private final ZFile zip;

  /** Has the zip file been closed? */
  private boolean closed;

  /** Predicate defining which files should not be compressed. */
  private final Predicate<String> noCompressPredicate;

  /**
   * Creates a new creator.
   *
   * @param creationData the data needed to create the APK
   * @param options zip file options
   * @throws IOException failed to create the zip
   */
  ApkZFileCreator(ApkCreatorFactory.CreationData creationData, ZFileOptions options)
      throws IOException {

    switch (creationData.getNativeLibrariesPackagingMode()) {
      case COMPRESSED:
        noCompressPredicate = creationData.getNoCompressPredicate();
        break;
      case UNCOMPRESSED_AND_ALIGNED:
        Predicate<String> baseNoCompressPredicate = creationData.getNoCompressPredicate();
        noCompressPredicate =
            name -> baseNoCompressPredicate.apply(name) || name.endsWith(NATIVE_LIBRARIES_SUFFIX);
        options.setAlignmentRule(AlignmentRules.compose(SO_RULE, options.getAlignmentRule()));
        break;
      default:
        throw new AssertionError();
    }
    // In case of incremental build we can skip validation since we generated the previous apk and
    // we trust ourselves
    options.setSkipValidation(creationData.isIncremental());

    zip =
        ZFiles.apk(
            creationData.getApkPath(),
            options,
            creationData.getSigningOptions(),
            creationData.getBuiltBy(),
            creationData.getCreatedBy());
    closed = false;
  }

  @Override
  public void writeZip(
      File zip, @Nullable Function<String, String> transform, @Nullable Predicate<String> isIgnored)
      throws IOException {
    Preconditions.checkState(!closed, "closed == true");
    Preconditions.checkArgument(zip.isFile(), "!zip.isFile()");

    Closer closer = Closer.create();
    try {
      ZFile toMerge = closer.register(ZFile.openReadWrite(zip));

      Predicate<String> ignorePredicate;
      if (isIgnored == null) {
        ignorePredicate = s -> false;
      } else {
        ignorePredicate = isIgnored;
      }

      // Files that *must* be uncompressed in the result should not be merged and should be
      // added after. This is just very slightly less efficient than ignoring just the ones
      // that were compressed and must be uncompressed, but it is a lot simpler :)
      Predicate<String> noMergePredicate =
          v -> ignorePredicate.apply(v) || noCompressPredicate.apply(v);

      this.zip.mergeFrom(toMerge, noMergePredicate);

      for (StoredEntry toMergeEntry : toMerge.entries()) {
        String path = toMergeEntry.getCentralDirectoryHeader().getName();
        if (noCompressPredicate.apply(path) && !ignorePredicate.apply(path)) {
          // This entry *must* be uncompressed so it was ignored in the merge and should
          // now be added to the apk.
          try (InputStream ignoredData = toMergeEntry.open()) {
            this.zip.add(path, ignoredData, false);
          }
        }
      }
    } catch (Throwable t) {
      throw closer.rethrow(t);
    } finally {
      closer.close();
    }
  }

  @Override
  public void writeFile(File inputFile, String apkPath) throws IOException {
    Preconditions.checkState(!closed, "closed == true");

    boolean mayCompress = !noCompressPredicate.apply(apkPath);

    Closer closer = Closer.create();
    try {
      FileInputStream inputFileStream = closer.register(new FileInputStream(inputFile));
      zip.add(apkPath, inputFileStream, mayCompress);
    } catch (IOException e) {
      throw closer.rethrow(e, IOException.class);
    } catch (Throwable t) {
      throw closer.rethrow(t);
    } finally {
      closer.close();
    }
  }

  @Override
  public void deleteFile(String apkPath) throws IOException {
    Preconditions.checkState(!closed, "closed == true");

    StoredEntry entry = zip.get(apkPath);
    if (entry != null) {
      entry.delete();
    }
  }

  @Override
  public boolean hasPendingChangesWithWait() throws IOException {
    return zip.hasPendingChangesWithWait();
  }

  @Override
  public void close() throws IOException {
    if (closed) {
      return;
    }

    zip.close();
    closed = true;
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkZFileCreatorFactory.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

import com.android.tools.build.apkzlib.utils.IOExceptionWrapper;
import com.android.tools.build.apkzlib.zip.ZFileOptions;
import java.io.IOException;

/** Creates instances of {@link ApkZFileCreator}. */
public class ApkZFileCreatorFactory implements ApkCreatorFactory {

  /** Options for the {@link ZFileOptions} to use in all APKs. */
  private final ZFileOptions options;

  /**
   * Creates a new factory.
   *
   * @param options the options to use for all instances created
   */
  public ApkZFileCreatorFactory(ZFileOptions options) {
    this.options = options;
  }

  @Override
  public ApkCreator make(CreationData creationData) {
    try {
      return new ApkZFileCreator(creationData, options);
    } catch (IOException e) {
      throw new IOExceptionWrapper(e);
    }
  }
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ManifestAttributes.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

/** Java manifest attributes and some default values. */
public interface ManifestAttributes {
  /** Manifest attribute with the built by information. */
  String BUILT_BY = "Built-By";

  /** Manifest attribute with the created by information. */
  String CREATED_BY = "Created-By";

  /** Manifest attribute with the manifest version. */
  String MANIFEST_VERSION = "Manifest-Version";

  /** Manifest attribute value with the manifest version. */
  String CURRENT_MANIFEST_VERSION = "1.0";
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/NativeLibrariesPackagingMode.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

/** Describes how native libs should be packaged. */
public enum NativeLibrariesPackagingMode {
  /** Native libs are packaged as any other file. */
  COMPRESSED,

  /**
   * Native libs are packaged uncompressed and page-aligned, so they can be mapped into memory at
   * runtime.
   *
   * <p>Support for this mode was added in Android 23, it only works if the {@code
   * extractNativeLibs} attribute is set in the manifest.
   */
  UNCOMPRESSED_AND_ALIGNED;
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ZFiles.java
================================================
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.build.apkzlib.zfile;

import com.android.tools.build.apkzlib.sign.ManifestGenerationExtension;
import com.android.tools.build.apkzlib.sign.SigningExtension;
import com.android.tools.build.apkzlib.sign.SigningOptions;
import com.android.tools.build.apkzlib.zip.AlignmentRule;
import com.android.tools.build.apkzlib.zip.AlignmentRules;
import com.android.tools.build.apkzlib.zip.ZFile;
import com.android.tools.build.apkzlib.zip.ZFileOptions;
import com.google.common.base.Optional;
import java.io.File;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.annotation.Nullable;

/** Factory for {@link ZFile}s that are specifically configured to be APKs, AARs, ... */
public class ZFiles {

  /** By default all non-compressed files are alignment at 4 byte boundaries.. */
  private static final AlignmentRule APK_DEFAULT_RULE = AlignmentRules.constant(4);

  /** Default build by string. */
  private static final String DEFAULT_BUILD_BY = "Generated-by-ADT";

  /** Default created by string. */
  private static final String DEFAULT_CREATED_BY = "Generated-by-ADT";

  /**
   * Creates a new zip file configured as an apk, based on a given file.
   *
   * @param f the file, if this path does not represent an existing path, will create a {@link
   *     ZFile} based on an non-existing path (a zip will be created when {@link ZFile#close()} is
   *     invoked)
   * @param options the options to create the {@link ZFile}
   * @return the zip file
   * @throws IOException failed to create the zip file
   */
  public static ZFile apk(File f, ZFileOptions options) throws IOException {
    options.setAlignmentRule(AlignmentRules.compose(options.getAlignmentRule(), APK_DEFAULT_RULE));
    return ZFile.openReadWrite(f, options);
  }

  /**
   * Creates a new zip file configured as an apk, based on a given file.
   *
   * @param f the file, if this path does not represent an existing path, will create a {@link
   *     ZFile} based on an non-existing path (a zip will be created when {@link ZFile#close()} is
   *     invoked)
   * @param options the options to create the {@link ZFile}
   * @param signingOptions the options to sign the apk
   * @param builtBy who to mark as builder in the manifest
   * @param createdBy who to mark as creator in the manifest
   * @return the zip file
   * @throws IOException failed to create the zip file
   */
  public static ZFile apk(
      File f,
      ZFileOptions options,
      Optional<SigningOptions> signingOptions,
      @Nullable String builtBy,
      @Nullable String createdBy)
      throws IOException {
    return apk(
        f, options, signingOptions, builtBy, createdBy, options.getAlwaysGenerateJarManifest());
  }

  /**
   * Creates a new zip file configured as an apk, based on a given file.
   *
   * @param f the file, if this path does not represent an existing path, will create a {@link
   *     ZFile} based on an non-existing path (a zip will be created when {@link ZFile#close()} is
   *     invoked)
   * @param options the options to create the {@link ZFile}
   * @param signingOptions the options to sign the apk
   * @param builtBy who to mark as builder in the manifest
   * @param createdBy who to mark as creator in the manifest
   * @param writeManifest a migration parameter that forces keeping (useless) manifest.mf file in
   *     apk file in order to prevent breaking changes. Clients of the previous interface will still
   *     get apk with manifest.mf because the flag is true by default
   * @return the zip file
   * @throws IOException failed to create the zip file
   * @deprecated Use ZFileOptions.setAlwaysGenerateJarManifest() instead.
   */
  @Deprecated
  // This method can be removed once ZFileOptions.getAlwaysGenerateJarManifest() is on Maven.
  public static ZFile apk(
      File f,
      ZFileOptions options,
      Optional<SigningOptions> signingOptions,
      @Nullable String builtBy,
      @Nullable String createdBy,
      boolean writeManifest)
      throws IOException {
    ZFile zfile = apk(f, options);

    if ((signingOptions.isPresent() && signingOptions.get().isV1SigningEnabled())
        || writeManifest) {
      if (builtBy == null) {
        builtBy = DEFAULT_BUILD_BY;
      }

      if (createdBy == null) {
        createdBy = DEFAULT_CREATED_BY;
      }
      ManifestGenerationExtension manifestExt = new ManifestGenerationExtension(builtBy, createdBy);
      manifestExt.register(zfile);
    }

    if (signingOptions.isPresent()) {
      SigningOptions signOptions = signingOptions.get();
      try {
        new SigningExtension(signOptions).register(zfile);
      } catch (NoSuchAlgorithmException | InvalidKeyException e) {
        throw new IOException("Failed to create signature extensions", e);
      }
    }

    return zfile;
  }

  private ZFiles() {}
}


================================================
FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/package-info.java
================================================
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed u
Download .txt
gitextract_ca_6zfem/

├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   └── feature_request.yml
│   └── workflows/
│       ├── crowdin.yml
│       └── main.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── apkzlib/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── android/
│                       └── tools/
│                           └── build/
│                               └── apkzlib/
│                                   ├── bytestorage/
│                                   │   ├── AbstractCloseableByteSourceFromOutputStreamBuilder.java
│                                   │   ├── ByteStorage.java
│                                   │   ├── ByteStorageFactory.java
│                                   │   ├── ChunkBasedByteStorage.java
│                                   │   ├── ChunkBasedByteStorageFactory.java
│                                   │   ├── ChunkBasedCloseableByteSource.java
│                                   │   ├── CloseableByteSourceFromOutputStreamBuilder.java
│                                   │   ├── InMemoryByteStorage.java
│                                   │   ├── InMemoryByteStorageFactory.java
│                                   │   ├── LimitedInputStream.java
│                                   │   ├── LruTrackedCloseableByteSource.java
│                                   │   ├── LruTracker.java
│                                   │   ├── OverflowToDiskByteStorage.java
│                                   │   ├── OverflowToDiskByteStorageFactory.java
│                                   │   ├── SwitchableDelegateCloseableByteSource.java
│                                   │   ├── SwitchableDelegateInputStream.java
│                                   │   ├── TemporaryDirectory.java
│                                   │   ├── TemporaryDirectoryFactory.java
│                                   │   ├── TemporaryDirectoryStorage.java
│                                   │   ├── TemporaryFile.java
│                                   │   └── TemporaryFileCloseableByteSource.java
│                                   ├── sign/
│                                   │   ├── DigestAlgorithm.java
│                                   │   ├── ManifestGenerationExtension.java
│                                   │   ├── SignatureAlgorithm.java
│                                   │   ├── SigningExtension.java
│                                   │   ├── SigningOptions.java
│                                   │   └── package-info.java
│                                   ├── utils/
│                                   │   ├── ApkZLibPair.java
│                                   │   ├── CachedFileContents.java
│                                   │   ├── CachedSupplier.java
│                                   │   ├── IOExceptionConsumer.java
│                                   │   ├── IOExceptionFunction.java
│                                   │   ├── IOExceptionRunnable.java
│                                   │   ├── IOExceptionWrapper.java
│                                   │   ├── SigningBlockUtils.java
│                                   │   └── package-info.java
│                                   ├── zfile/
│                                   │   ├── ApkCreator.java
│                                   │   ├── ApkCreatorFactory.java
│                                   │   ├── ApkZFileCreator.java
│                                   │   ├── ApkZFileCreatorFactory.java
│                                   │   ├── ManifestAttributes.java
│                                   │   ├── NativeLibrariesPackagingMode.java
│                                   │   ├── ZFiles.java
│                                   │   └── package-info.java
│                                   └── zip/
│                                       ├── AlignmentRule.java
│                                       ├── AlignmentRules.java
│                                       ├── CentralDirectory.java
│                                       ├── CentralDirectoryHeader.java
│                                       ├── CentralDirectoryHeaderCompressInfo.java
│                                       ├── CompressionMethod.java
│                                       ├── CompressionResult.java
│                                       ├── Compressor.java
│                                       ├── DataDescriptorType.java
│                                       ├── EncodeUtils.java
│                                       ├── Eocd.java
│                                       ├── EocdGroup.java
│                                       ├── ExtraField.java
│                                       ├── FileUseMap.java
│                                       ├── FileUseMapEntry.java
│                                       ├── GPFlags.java
│                                       ├── InflaterByteSource.java
│                                       ├── LazyDelegateByteSource.java
│                                       ├── NestedZip.java
│                                       ├── ProcessedAndRawByteSources.java
│                                       ├── StoredEntry.java
│                                       ├── StoredEntryType.java
│                                       ├── VerifyLog.java
│                                       ├── VerifyLogs.java
│                                       ├── ZFile.java
│                                       ├── ZFileExtension.java
│                                       ├── ZFileOptions.java
│                                       ├── Zip64Eocd.java
│                                       ├── Zip64EocdLocator.java
│                                       ├── Zip64ExtensibleDataSector.java
│                                       ├── ZipField.java
│                                       ├── ZipFieldInvariant.java
│                                       ├── ZipFieldInvariantMaxValue.java
│                                       ├── ZipFieldInvariantMinValue.java
│                                       ├── ZipFieldInvariantNonNegative.java
│                                       ├── ZipFileState.java
│                                       ├── compress/
│                                       │   ├── BestAndDefaultDeflateExecutorCompressor.java
│                                       │   ├── DeflateExecutionCompressor.java
│                                       │   ├── ExecutorCompressor.java
│                                       │   ├── Zip64NotSupportedException.java
│                                       │   └── package-info.java
│                                       └── utils/
│                                           ├── ByteTracker.java
│                                           ├── CloseableByteSource.java
│                                           ├── CloseableDelegateByteSource.java
│                                           ├── LittleEndianUtils.java
│                                           ├── MsDosDateTimeUtils.java
│                                           └── RandomAccessFileUtils.java
├── build.gradle.kts
├── crowdin.yml
├── gradle/
│   ├── lspatch.versions.toml
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jar/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── assets/
│               └── keystore
├── manager/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules-debug.pro
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── assets/
│           │   └── keystore
│           ├── java/
│           │   └── org/
│           │       └── lsposed/
│           │           └── lspatch/
│           │               ├── LSPApplication.kt
│           │               ├── Patcher.kt
│           │               ├── config/
│           │               │   ├── ConfigManager.kt
│           │               │   ├── Configs.kt
│           │               │   └── MyKeyStore.kt
│           │               ├── database/
│           │               │   ├── LSPDatabase.kt
│           │               │   ├── dao/
│           │               │   │   ├── ModuleDao.kt
│           │               │   │   └── ScopeDao.kt
│           │               │   └── entity/
│           │               │       ├── Module.kt
│           │               │       └── Scope.kt
│           │               ├── manager/
│           │               │   ├── AppBroadcastReceiver.kt
│           │               │   ├── ManagerService.kt
│           │               │   └── ModuleService.kt
│           │               ├── ui/
│           │               │   ├── activity/
│           │               │   │   └── MainActivity.kt
│           │               │   ├── component/
│           │               │   │   ├── AnywhereDropdown.kt
│           │               │   │   ├── AppItem.kt
│           │               │   │   ├── CenterTopBar.kt
│           │               │   │   ├── LoadingDialog.kt
│           │               │   │   ├── SearchBar.kt
│           │               │   │   ├── SelectionColumn.kt
│           │               │   │   ├── Shimmer.kt
│           │               │   │   └── settings/
│           │               │   │       ├── CheckBox.kt
│           │               │   │       ├── Slot.kt
│           │               │   │       └── Switch.kt
│           │               │   ├── page/
│           │               │   │   ├── BottomBarDestination.kt
│           │               │   │   ├── HomeScreen.kt
│           │               │   │   ├── LogsScreen.kt
│           │               │   │   ├── ManageScreen.kt
│           │               │   │   ├── NewPatchScreen.kt
│           │               │   │   ├── RepoScreen.kt
│           │               │   │   ├── SelectAppsScreen.kt
│           │               │   │   ├── SettingsScreen.kt
│           │               │   │   └── manage/
│           │               │   │       ├── AppManagePage.kt
│           │               │   │       └── ModuleManagePage.kt
│           │               │   ├── theme/
│           │               │   │   ├── Theme.kt
│           │               │   │   └── Type.kt
│           │               │   ├── util/
│           │               │   │   ├── CompositionProvider.kt
│           │               │   │   ├── HtmlText.kt
│           │               │   │   ├── MultiDelegateState.kt
│           │               │   │   ├── Preview.kt
│           │               │   │   └── Utils.kt
│           │               │   ├── viewmodel/
│           │               │   │   ├── NewPatchViewModel.kt
│           │               │   │   ├── SelectAppsViewModel.kt
│           │               │   │   └── manage/
│           │               │   │       ├── AppManageViewModel.kt
│           │               │   │       └── ModuleManageViewModel.kt
│           │               │   └── viewstate/
│           │               │       └── ProcessingState.kt
│           │               └── util/
│           │                   ├── IntentSenderHelper.kt
│           │                   ├── LSPPackageManager.kt
│           │                   └── ShizukuApi.kt
│           └── res/
│               ├── drawable/
│               │   ├── ic_launcher_background.xml
│               │   └── ic_launcher_foreground.xml
│               ├── drawable-zh-rCN/
│               │   └── ic_launcher_background.xml
│               ├── drawable-zh-rTW/
│               │   └── ic_launcher_background.xml
│               ├── mipmap-anydpi-v26/
│               │   └── ic_launcher.xml
│               ├── values/
│               │   ├── strings.xml
│               │   └── strings_untranslatable.xml
│               ├── values-af/
│               │   └── strings.xml
│               ├── values-ar/
│               │   └── strings.xml
│               ├── values-bg/
│               │   └── strings.xml
│               ├── values-bn/
│               │   └── strings.xml
│               ├── values-ca/
│               │   └── strings.xml
│               ├── values-cs/
│               │   └── strings.xml
│               ├── values-da/
│               │   └── strings.xml
│               ├── values-de/
│               │   └── strings.xml
│               ├── values-el/
│               │   └── strings.xml
│               ├── values-es/
│               │   └── strings.xml
│               ├── values-et/
│               │   └── strings.xml
│               ├── values-fa/
│               │   └── strings.xml
│               ├── values-fi/
│               │   └── strings.xml
│               ├── values-fr/
│               │   └── strings.xml
│               ├── values-hi/
│               │   └── strings.xml
│               ├── values-hr/
│               │   └── strings.xml
│               ├── values-hu/
│               │   └── strings.xml
│               ├── values-in/
│               │   └── strings.xml
│               ├── values-it/
│               │   └── strings.xml
│               ├── values-iw/
│               │   └── strings.xml
│               ├── values-ja/
│               │   └── strings.xml
│               ├── values-ko/
│               │   └── strings.xml
│               ├── values-ku/
│               │   └── strings.xml
│               ├── values-lt/
│               │   └── strings.xml
│               ├── values-nl/
│               │   └── strings.xml
│               ├── values-no/
│               │   └── strings.xml
│               ├── values-pl/
│               │   └── strings.xml
│               ├── values-pt/
│               │   └── strings.xml
│               ├── values-pt-rBR/
│               │   └── strings.xml
│               ├── values-ro/
│               │   └── strings.xml
│               ├── values-ru/
│               │   └── strings.xml
│               ├── values-si/
│               │   └── strings.xml
│               ├── values-sk/
│               │   └── strings.xml
│               ├── values-sv/
│               │   └── strings.xml
│               ├── values-th/
│               │   └── strings.xml
│               ├── values-tr/
│               │   └── strings.xml
│               ├── values-uk/
│               │   └── strings.xml
│               ├── values-ur/
│               │   └── strings.xml
│               ├── values-vi/
│               │   └── strings.xml
│               ├── values-zh-rCN/
│               │   └── strings.xml
│               ├── values-zh-rHK/
│               │   └── strings.xml
│               └── values-zh-rTW/
│                   └── strings.xml
├── meta-loader/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           └── java/
│               └── org/
│                   └── lsposed/
│                       └── lspatch/
│                           └── metaloader/
│                               └── LSPAppComponentFactoryStub.java
├── patch/
│   ├── .gitignore
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── java/
│               └── org/
│                   └── lsposed/
│                       └── patch/
│                           ├── LSPatch.java
│                           └── util/
│                               ├── ApkSignatureHelper.java
│                               ├── JavaLogger.java
│                               ├── Logger.java
│                               └── ManifestParser.java
├── patch-loader/
│   ├── .gitignore
│   ├── build.gradle.kts
│   ├── proguard-rules.pro
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/
│           │   └── org/
│           │       └── lsposed/
│           │           ├── lspatch/
│           │           │   ├── loader/
│           │           │   │   ├── LSPApplication.java
│           │           │   │   ├── LSPLoader.java
│           │           │   │   ├── SigBypass.java
│           │           │   │   └── util/
│           │           │   │       ├── FileUtils.java
│           │           │   │       └── XLog.java
│           │           │   └── service/
│           │           │       ├── LocalApplicationService.java
│           │           │       └── RemoteApplicationService.java
│           │           └── lspd/
│           │               └── nativebridge/
│           │                   └── SigBypass.java
│           └── jni/
│               ├── CMakeLists.txt
│               ├── api/
│               │   └── patch_main.cpp
│               ├── include/
│               │   └── art/
│               │       └── runtime/
│               │           ├── jit/
│               │           │   └── profile_saver.h
│               │           └── oat_file_manager.h
│               └── src/
│                   ├── config_impl.h
│                   ├── jni/
│                   │   ├── bypass_sig.cpp
│                   │   └── bypass_sig.h
│                   ├── patch_loader.cpp
│                   └── patch_loader.h
├── settings.gradle.kts
└── share/
    ├── android/
    │   ├── .gitignore
    │   ├── build.gradle.kts
    │   └── src/
    │       └── main/
    │           └── java/
    │               └── org/
    │                   └── lsposed/
    │                       └── lspatch/
    │                           └── util/
    │                               └── ModuleLoader.java
    └── java/
        ├── .gitignore
        ├── build.gradle.kts
        └── src/
            ├── main/
            │   └── java/
            │       └── org/
            │           └── lsposed/
            │               └── lspatch/
            │                   └── share/
            │                       ├── Constants.java
            │                       └── PatchConfig.java
            └── template/
                └── java/
                    └── org.lsposed.lspatch.share/
                        └── LSPConfig.java
Download .txt
SYMBOL INDEX (841 symbols across 113 files)

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/AbstractCloseableByteSourceFromOutputStreamBuilder.java
  class AbstractCloseableByteSourceFromOutputStreamBuilder (line 14) | abstract class AbstractCloseableByteSourceFromOutputStreamBuilder
    method AbstractCloseableByteSourceFromOutputStreamBuilder (line 35) | AbstractCloseableByteSourceFromOutputStreamBuilder() {
    method write (line 41) | @Override
    method write (line 47) | @Override
    method close (line 53) | @Override
    method build (line 58) | @Override
    method doWrite (line 76) | protected abstract void doWrite(byte[] b, int off, int len) throws IOE...
    method doBuild (line 85) | protected abstract CloseableByteSource doBuild() throws IOException;

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ByteStorage.java
  type ByteStorage (line 30) | public interface ByteStorage extends Closeable {
    method fromStream (line 38) | CloseableByteSource fromStream(InputStream stream) throws IOException;
    method makeBuilder (line 48) | CloseableByteSourceFromOutputStreamBuilder makeBuilder() throws IOExce...
    method fromSource (line 57) | CloseableByteSource fromSource(ByteSource source) throws IOException;
    method getBytesUsed (line 64) | long getBytesUsed();
    method getMaxBytesUsed (line 71) | long getMaxBytesUsed();

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ByteStorageFactory.java
  type ByteStorageFactory (line 6) | public interface ByteStorageFactory {
    method create (line 13) | ByteStorage create() throws IOException;

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedByteStorage.java
  class ChunkBasedByteStorage (line 17) | public class ChunkBasedByteStorage implements ByteStorage {
    method ChunkBasedByteStorage (line 32) | ChunkBasedByteStorage(ByteStorage delegate) {
    method ChunkBasedByteStorage (line 40) | ChunkBasedByteStorage(long maxChunkSize, ByteStorage delegate) {
    method getDelegate (line 46) | @VisibleForTesting // private otherwise.
    method fromStream (line 51) | @Override
    method makeBuilder (line 65) | @Override
    method fromSource (line 114) | @Override
    method getBytesUsed (line 129) | @Override
    method getMaxBytesUsed (line 134) | @Override
    method close (line 139) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedByteStorageFactory.java
  class ChunkBasedByteStorageFactory (line 10) | public class ChunkBasedByteStorageFactory implements ByteStorageFactory {
    method ChunkBasedByteStorageFactory (line 19) | public ChunkBasedByteStorageFactory(ByteStorageFactory delegate) {
    method ChunkBasedByteStorageFactory (line 27) | public ChunkBasedByteStorageFactory(ByteStorageFactory delegate, @Null...
    method create (line 32) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedCloseableByteSource.java
  class ChunkBasedCloseableByteSource (line 15) | class ChunkBasedCloseableByteSource extends CloseableDelegateByteSource {
    method ChunkBasedCloseableByteSource (line 21) | ChunkBasedCloseableByteSource(List<CloseableByteSource> sources) throw...
    method sumSizes (line 27) | private static long sumSizes(List<CloseableByteSource> sources) throws...
    method innerClose (line 36) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/CloseableByteSourceFromOutputStreamBuilder.java
  class CloseableByteSourceFromOutputStreamBuilder (line 11) | public abstract class CloseableByteSourceFromOutputStreamBuilder extends...
    method build (line 21) | public abstract CloseableByteSource build() throws IOException;

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/InMemoryByteStorage.java
  class InMemoryByteStorage (line 28) | public class InMemoryByteStorage implements ByteStorage {
    method fromStream (line 36) | @Override
    method makeBuilder (line 49) | @Override
    method fromSource (line 73) | @Override
    method updateUsage (line 83) | private synchronized void updateUsage(long delta) {
    method getBytesUsed (line 90) | @Override
    method getMaxBytesUsed (line 95) | @Override
    method close (line 100) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/InMemoryByteStorageFactory.java
  class InMemoryByteStorageFactory (line 9) | public class InMemoryByteStorageFactory implements ByteStorageFactory {
    method create (line 11) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LimitedInputStream.java
  class LimitedInputStream (line 29) | class LimitedInputStream extends InputStream {
    method LimitedInputStream (line 45) | LimitedInputStream(InputStream input, long maximum) {
    method read (line 51) | @Override
    method read (line 67) | @Override
    method isInputFinished (line 85) | boolean isInputFinished() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LruTrackedCloseableByteSource.java
  class LruTrackedCloseableByteSource (line 17) | class LruTrackedCloseableByteSource extends SwitchableDelegateCloseableB...
    method LruTrackedCloseableByteSource (line 28) | LruTrackedCloseableByteSource(
    method openStream (line 38) | @Override
    method innerClose (line 48) | @Override
    method untrack (line 60) | private synchronized void untrack() {
    method move (line 71) | synchronized void move(ByteStorage diskStorage) throws IOException {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LruTracker.java
  class LruTracker (line 20) | class LruTracker<T> {
    method LruTracker (line 36) | LruTracker() {
    method track (line 43) | synchronized void track(T object) {
    method untrack (line 51) | synchronized void untrack(T object) {
    method access (line 58) | synchronized void access(T object) {
    method positionOf (line 67) | synchronized int positionOf(T object) {
    method last (line 77) | @Nullable

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/OverflowToDiskByteStorage.java
  class OverflowToDiskByteStorage (line 20) | public class OverflowToDiskByteStorage implements ByteStorage {
    method OverflowToDiskByteStorage (line 50) | public OverflowToDiskByteStorage(TemporaryDirectoryFactory temporaryDi...
    method OverflowToDiskByteStorage (line 66) | public OverflowToDiskByteStorage(
    method fromStream (line 75) | @Override
    method makeBuilder (line 84) | @Override
    method fromSource (line 104) | @Override
    method getBytesUsed (line 113) | @Override
    method getMaxBytesUsed (line 118) | @Override
    method checkMaxUsage (line 124) | private synchronized void checkMaxUsage() {
    method reviewSources (line 131) | private synchronized void reviewSources() throws IOException {
    method getMemoryBytesUsed (line 143) | public long getMemoryBytesUsed() {
    method getMaxMemoryBytesUsed (line 148) | public long getMaxMemoryBytesUsed() {
    method getDiskBytesUsed (line 153) | public long getDiskBytesUsed() {
    method getMaxDiskBytesUsed (line 158) | public long getMaxDiskBytesUsed() {
    method close (line 162) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/OverflowToDiskByteStorageFactory.java
  class OverflowToDiskByteStorageFactory (line 10) | public class OverflowToDiskByteStorageFactory implements ByteStorageFact...
    method OverflowToDiskByteStorageFactory (line 24) | public OverflowToDiskByteStorageFactory(TemporaryDirectoryFactory temp...
    method OverflowToDiskByteStorageFactory (line 36) | public OverflowToDiskByteStorageFactory(
    method create (line 42) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/SwitchableDelegateCloseableByteSource.java
  class SwitchableDelegateCloseableByteSource (line 17) | class SwitchableDelegateCloseableByteSource extends CloseableByteSource {
    method SwitchableDelegateCloseableByteSource (line 32) | SwitchableDelegateCloseableByteSource(CloseableByteSource source) {
    method innerClose (line 37) | @Override
    method openStream (line 52) | @Override
    method switchSource (line 89) | synchronized void switchSource(CloseableByteSource source) throws IOEx...

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/SwitchableDelegateInputStream.java
  class SwitchableDelegateInputStream (line 19) | class SwitchableDelegateInputStream extends InputStream {
    method SwitchableDelegateInputStream (line 40) | SwitchableDelegateInputStream(InputStream delegate) {
    method skipDataIfNeeded (line 51) | private void skipDataIfNeeded() throws IOException {
    method increaseOffset (line 63) | private int increaseOffset(int amount) {
    method increaseOffset (line 72) | private long increaseOffset(long amount) {
    method read (line 84) | @Override
    method read (line 94) | @Override
    method read (line 104) | @Override
    method skip (line 121) | @Override
    method available (line 131) | @Override
    method close (line 141) | @Override
    method mark (line 147) | @Override
    method reset (line 152) | @Override
    method markSupported (line 157) | @Override
    method switchStream (line 171) | synchronized void switchStream(InputStream newStream) throws IOExcepti...

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectory.java
  type TemporaryDirectory (line 15) | public interface TemporaryDirectory extends Closeable {
    method newFile (line 22) | File newFile() throws IOException;
    method getDirectory (line 25) | @VisibleForTesting // private otherwise.
    method newSystemTemporaryDirectory (line 33) | static TemporaryDirectory newSystemTemporaryDirectory() throws IOExcep...
    method fixed (line 61) | static TemporaryDirectory fixed(File directory) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectoryFactory.java
  type TemporaryDirectoryFactory (line 10) | public interface TemporaryDirectoryFactory {
    method make (line 18) | TemporaryDirectory make() throws IOException;
    method fixed (line 28) | static TemporaryDirectoryFactory fixed(File directory) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectoryStorage.java
  class TemporaryDirectoryStorage (line 16) | public class TemporaryDirectoryStorage implements ByteStorage {
    method TemporaryDirectoryStorage (line 35) | public TemporaryDirectoryStorage(TemporaryDirectoryFactory temporaryDi...
    method fromStream (line 40) | @Override
    method makeBuilder (line 52) | @Override
    method fromSource (line 73) | @Override
    method getBytesUsed (line 80) | @Override
    method getMaxBytesUsed (line 85) | @Override
    method incrementBytesUsed (line 91) | private synchronized void incrementBytesUsed(long amount) {
    method close (line 98) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryFile.java
  class TemporaryFile (line 12) | public class TemporaryFile implements Closeable {
    method TemporaryFile (line 27) | public TemporaryFile(File file) {
    method getFile (line 33) | public File getFile() {
    method close (line 38) | @Override
    method deleteFile (line 50) | private void deleteFile(File file) throws IOException {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryFileCloseableByteSource.java
  class TemporaryFileCloseableByteSource (line 12) | class TemporaryFileCloseableByteSource extends CloseableDelegateByteSour...
    method TemporaryFileCloseableByteSource (line 25) | TemporaryFileCloseableByteSource(File file, Runnable closeCallback) {
    method innerClose (line 31) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/DigestAlgorithm.java
  type DigestAlgorithm (line 21) | public enum DigestAlgorithm {
    method DigestAlgorithm (line 64) | DigestAlgorithm(String attributeName, String messageDigestName) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/ManifestGenerationExtension.java
  class ManifestGenerationExtension (line 51) | public class ManifestGenerationExtension {
    method ManifestGenerationExtension (line 104) | public ManifestGenerationExtension(String builtBy, String createdBy) {
    method markDirty (line 127) | private void markDirty() {
    method register (line 138) | public void register(ZFile zFile) throws IOException {
    method rebuildManifest (line 157) | private void rebuildManifest() throws IOException {
    method setMainAttribute (line 198) | private void setMainAttribute(String attribute, String value) {
    method updateManifest (line 212) | private void updateManifest() throws IOException {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SignatureAlgorithm.java
  type SignatureAlgorithm (line 22) | public enum SignatureAlgorithm {
    method SignatureAlgorithm (line 48) | SignatureAlgorithm(String keyAlgorithm, int minSdkVersion, String sign...
    method fromKeyAlgorithm (line 64) | public static SignatureAlgorithm fromKeyAlgorithm(String keyAlgorithm,...
    method signatureAlgorithmName (line 91) | public String signatureAlgorithmName(DigestAlgorithm digestAlgorithm) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SigningExtension.java
  class SigningExtension (line 62) | public class SigningExtension {
    method SigningExtension (line 123) | public SigningExtension(SigningOptions opts) throws InvalidKeyException {
    method register (line 145) | public void register(ZFile zFile) throws NoSuchAlgorithmException, IOE...
    method isCurrentSignatureAsRequested (line 208) | private boolean isCurrentSignatureAsRequested() throws IOException, No...
    method onZipEntryOutput (line 257) | private void onZipEntryOutput(StoredEntry entry) throws IOException {
    method copyStreamToDataSink (line 275) | private void copyStreamToDataSink(InputStream inputStream, DataSink da...
    method onZipEntryRemovedFromOutput (line 283) | private void onZipEntryRemovedFromOutput(String entryName) {
    method onOutputZipReadyForUpdate (line 289) | private void onOutputZipReadyForUpdate() throws IOException {
    method onOutputZipEntriesWritten (line 354) | private void onOutputZipEntriesWritten() throws IOException {
    method onOutputClosed (line 425) | private void onOutputClosed() {
    method setDirty (line 433) | private void setDirty() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SigningOptions.java
  class SigningOptions (line 29) | @AutoValue
    class Builder (line 33) | @AutoValue.Builder
      method setKey (line 35) | public abstract Builder setKey(@Nonnull PrivateKey key);
      method setCertificates (line 36) | public abstract Builder setCertificates(@Nonnull ImmutableList<X509C...
      method setCertificates (line 37) | public abstract Builder setCertificates(X509Certificate... certs);
      method setV1SigningEnabled (line 38) | public abstract Builder setV1SigningEnabled(boolean enabled);
      method setV2SigningEnabled (line 39) | public abstract Builder setV2SigningEnabled(boolean enabled);
      method setMinSdkVersion (line 40) | public abstract Builder setMinSdkVersion(int version);
      method setValidation (line 41) | public abstract Builder setValidation(@Nonnull Validation validation);
      method setExecutor (line 42) | public abstract Builder setExecutor(@Nullable RunnablesExecutor exec...
      method setSdkDependencyData (line 43) | public abstract Builder setSdkDependencyData(@Nullable byte[] sdkDep...
      method autoBuild (line 45) | abstract SigningOptions autoBuild();
      method build (line 47) | public SigningOptions build() {
    method builder (line 57) | public static Builder builder() {
    method getKey (line 65) | public abstract PrivateKey getKey();
    method getCertificates (line 71) | public abstract ImmutableList<X509Certificate> getCertificates();
    method isV1SigningEnabled (line 74) | public abstract boolean isV1SigningEnabled();
    method isV2SigningEnabled (line 77) | public abstract boolean isV2SigningEnabled();
    method getMinSdkVersion (line 80) | public abstract int getMinSdkVersion();
    method getValidation (line 83) | public abstract Validation getValidation();
    method getExecutor (line 85) | @Nullable
    method getSdkDependencyData (line 89) | @SuppressWarnings("mutable")
    type Validation (line 93) | public enum Validation {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/ApkZLibPair.java
  class ApkZLibPair (line 20) | public class ApkZLibPair<T1, T2> {
    method ApkZLibPair (line 34) | public ApkZLibPair(T1 v1, T2 v2) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/CachedFileContents.java
  class CachedFileContents (line 51) | public class CachedFileContents<T> {
    method CachedFileContents (line 74) | public CachedFileContents(File file) {
    method closed (line 86) | public void closed(@Nullable T cache) {
    method isValid (line 100) | public boolean isValid() {
    method getCache (line 133) | @Nullable
    method hashFile (line 143) | @Nullable
    method getFile (line 157) | public File getFile() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/CachedSupplier.java
  class CachedSupplier (line 45) | public class CachedSupplier<T> {
    method CachedSupplier (line 60) | public CachedSupplier(Supplier<T> supplier) {
    method get (line 70) | public synchronized T get() {
    method reset (line 82) | public synchronized void reset() {
    method precomputed (line 96) | public synchronized void precomputed(T t) {
    method isValid (line 106) | public synchronized boolean isValid() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionConsumer.java
  type IOExceptionConsumer (line 23) | public interface IOExceptionConsumer<T> {
    method accept (line 30) | void accept(@Nullable T input) throws IOException;

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionFunction.java
  type IOExceptionFunction (line 24) | public interface IOExceptionFunction<F, T> {
    method apply (line 32) | @Nullable
    method asFunction (line 40) | static <F, T> Function<F, T> asFunction(IOExceptionFunction<F, T> f) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionRunnable.java
  type IOExceptionRunnable (line 22) | public interface IOExceptionRunnable {
    method run (line 29) | void run() throws IOException;
    method asRunnable (line 36) | static Runnable asRunnable(IOExceptionRunnable r) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionWrapper.java
  class IOExceptionWrapper (line 25) | public class IOExceptionWrapper extends RuntimeException {
    method IOExceptionWrapper (line 32) | public IOExceptionWrapper(IOException e) {
    method getCause (line 36) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/SigningBlockUtils.java
  class SigningBlockUtils (line 39) | public final class SigningBlockUtils {
    method addToSigningBlock (line 60) | public static byte[] addToSigningBlock(byte[] signingBlock, byte[] blo...
    method appendToSigningBlock (line 81) | private static byte[] appendToSigningBlock(byte[] signingBlock, byte[]...
    method createSigningBlock (line 95) | private static byte[] createSigningBlock(byte[] blockValue, int blockI...
    method extractAllSigners (line 107) | private static ImmutableList<Pair<byte[], Integer>> extractAllSigners(...
    method extractBlock (line 149) | @Nullable
    method SigningBlockUtils (line 184) | private SigningBlockUtils() {}

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkCreator.java
  type ApkCreator (line 27) | public interface ApkCreator extends Closeable {
    method writeZip (line 42) | void writeZip(
    method writeFile (line 54) | void writeFile(File inputFile, String apkPath) throws IOException;
    method deleteFile (line 62) | void deleteFile(String apkPath) throws IOException;
    method hasPendingChangesWithWait (line 65) | boolean hasPendingChangesWithWait() throws IOException;

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkCreatorFactory.java
  type ApkCreatorFactory (line 29) | public interface ApkCreatorFactory {
    method make (line 36) | ApkCreator make(CreationData creationData);
    class CreationData (line 42) | @AutoValue
      class Builder (line 46) | @AutoValue.Builder
        method setApkPath (line 48) | public abstract Builder setApkPath(@Nonnull File apkPath);
        method setSigningOptions (line 50) | public abstract Builder setSigningOptions(@Nonnull SigningOptions ...
        method setBuiltBy (line 52) | public abstract Builder setBuiltBy(@Nullable String buildBy);
        method setCreatedBy (line 54) | public abstract Builder setCreatedBy(@Nullable String createdBy);
        method setNativeLibrariesPackagingMode (line 56) | public abstract Builder setNativeLibrariesPackagingMode(
        method setNoCompressPredicate (line 59) | public abstract Builder setNoCompressPredicate(Predicate<String> p...
        method setIncremental (line 61) | public abstract Builder setIncremental(boolean incremental);
        method autoBuild (line 63) | abstract CreationData autoBuild();
        method build (line 65) | public CreationData build() {
      method builder (line 72) | public static Builder builder() {
      method getApkPath (line 86) | public abstract File getApkPath();
      method getSigningOptions (line 93) | @Nonnull
      method getBuiltBy (line 101) | @Nullable
      method getCreatedBy (line 109) | @Nullable
      method getNativeLibrariesPackagingMode (line 113) | public abstract NativeLibrariesPackagingMode getNativeLibrariesPacka...
      method getNoCompressPredicate (line 116) | public abstract Predicate<String> getNoCompressPredicate();
      method isIncremental (line 126) | public abstract boolean isIncremental();

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkZFileCreator.java
  class ApkZFileCreator (line 35) | class ApkZFileCreator implements ApkCreator {
    method ApkZFileCreator (line 60) | ApkZFileCreator(ApkCreatorFactory.CreationData creationData, ZFileOpti...
    method writeZip (line 90) | @Override
    method writeFile (line 133) | @Override
    method deleteFile (line 152) | @Override
    method hasPendingChangesWithWait (line 162) | @Override
    method close (line 167) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkZFileCreatorFactory.java
  class ApkZFileCreatorFactory (line 24) | public class ApkZFileCreatorFactory implements ApkCreatorFactory {
    method ApkZFileCreatorFactory (line 34) | public ApkZFileCreatorFactory(ZFileOptions options) {
    method make (line 38) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ManifestAttributes.java
  type ManifestAttributes (line 20) | public interface ManifestAttributes {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/NativeLibrariesPackagingMode.java
  type NativeLibrariesPackagingMode (line 20) | public enum NativeLibrariesPackagingMode {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ZFiles.java
  class ZFiles (line 34) | public class ZFiles {
    method apk (line 55) | public static ZFile apk(File f, ZFileOptions options) throws IOExcepti...
    method apk (line 73) | public static ZFile apk(
    method apk (line 101) | @Deprecated
    method ZFiles (line 138) | private ZFiles() {}

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/AlignmentRule.java
  type AlignmentRule (line 21) | public interface AlignmentRule {
    method alignment (line 33) | int alignment(String path);

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/AlignmentRules.java
  class AlignmentRules (line 22) | public final class AlignmentRules {
    method AlignmentRules (line 24) | private AlignmentRules() {}
    method constant (line 32) | public static AlignmentRule constant(int alignment) {
    method constantForSuffix (line 46) | public static AlignmentRule constantForSuffix(String suffix, int align...
    method compose (line 62) | public static AlignmentRule compose(AlignmentRule... rules) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CentralDirectory.java
  class CentralDirectory (line 40) | class CentralDirectory {
    method CentralDirectory (line 179) | CentralDirectory(ZFile file) {
    method makeFromData (line 201) | static CentralDirectory makeFromData(ByteBuffer bytes, long count, ZFi...
    method makeFromEntries (line 234) | static CentralDirectory makeFromEntries(Set<StoredEntry> entries, ZFil...
    method readEntry (line 256) | private void readEntry(ByteBuffer bytes, ByteStorage storage) throws I...
    method getEntries (line 377) | Map<String, StoredEntry> getEntries() {
    method containsZip64Files (line 389) | boolean containsZip64Files() {
    method toBytes (line 399) | byte[] toBytes() throws IOException {
    method computeByteRepresentation (line 409) | private byte[] computeByteRepresentation() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CentralDirectoryHeader.java
  class CentralDirectoryHeader (line 35) | public class CentralDirectoryHeader implements Cloneable {
    method CentralDirectoryHeader (line 106) | CentralDirectoryHeader(
    method CentralDirectoryHeader (line 124) | CentralDirectoryHeader(
    method link (line 155) | public CentralDirectoryHeader link(String name, byte[] encodedFileName...
    method getName (line 179) | public String getName() {
    method getUncompressedSize (line 188) | public long getUncompressedSize() {
    method getCrc32 (line 197) | public long getCrc32() {
    method setCrc32 (line 206) | void setCrc32(long crc32) {
    method getMadeBy (line 215) | public long getMadeBy() {
    method setMadeBy (line 224) | void setMadeBy(long madeBy) {
    method getGpBit (line 233) | public GPFlags getGpBit() {
    method getLastModTime (line 243) | public long getLastModTime() {
    method setLastModTime (line 253) | void setLastModTime(long lastModTime) {
    method getLastModDate (line 263) | public long getLastModDate() {
    method setLastModDate (line 273) | void setLastModDate(long lastModDate) {
    method getExtraField (line 282) | public ExtraField getExtraField() {
    method setExtraField (line 291) | public void setExtraField(ExtraField extraField) {
    method setExtraFieldNoNotify (line 302) | void setExtraFieldNoNotify(ExtraField extraField) {
    method getComment (line 311) | public byte[] getComment() {
    method setComment (line 320) | void setComment(byte[] comment) {
    method getInternalAttributes (line 329) | public long getInternalAttributes() {
    method setInternalAttributes (line 338) | void setInternalAttributes(long internalAttributes) {
    method getExternalAttributes (line 347) | public long getExternalAttributes() {
    method setExternalAttributes (line 356) | void setExternalAttributes(long externalAttributes) {
    method getOffset (line 366) | public long getOffset() {
    method setOffset (line 375) | void setOffset(long offset) {
    method getEncodedFileName (line 384) | public byte[] getEncodedFileName() {
    method resetDeferredCrc (line 389) | void resetDeferredCrc() {
    method clone (line 397) | @Override
    method getCompressionInfo (line 411) | public Future<CentralDirectoryHeaderCompressInfo> getCompressionInfo() {
    method getCompressionInfoWithWait (line 422) | public CentralDirectoryHeaderCompressInfo getCompressionInfoWithWait()...

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CentralDirectoryHeaderCompressInfo.java
  class CentralDirectoryHeaderCompressInfo (line 24) | public class CentralDirectoryHeaderCompressInfo {
    method CentralDirectoryHeaderCompressInfo (line 55) | public CentralDirectoryHeaderCompressInfo(
    method CentralDirectoryHeaderCompressInfo (line 69) | public CentralDirectoryHeaderCompressInfo(
    method getCompressedSize (line 89) | public long getCompressedSize() {
    method getMethod (line 98) | public CompressionMethod getMethod() {
    method getVersionExtract (line 107) | long getVersionExtract() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CompressionMethod.java
  type CompressionMethod (line 22) | public enum CompressionMethod {
    method CompressionMethod (line 37) | CompressionMethod(int methodCode) {
    method fromCode (line 47) | @Nullable

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CompressionResult.java
  class CompressionResult (line 22) | public class CompressionResult {
    method CompressionResult (line 42) | public CompressionResult(CloseableByteSource source, CompressionMethod...
    method getCompressionMethod (line 53) | public CompressionMethod getCompressionMethod() {
    method getSource (line 62) | public CloseableByteSource getSource() {
    method getSize (line 71) | public long getSize() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Compressor.java
  type Compressor (line 28) | public interface Compressor {
    method compress (line 37) | ListenableFuture<CompressionResult> compress(CloseableByteSource sourc...

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/DataDescriptorType.java
  type DataDescriptorType (line 28) | public enum DataDescriptorType {
    method DataDescriptorType (line 46) | DataDescriptorType(int size) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/EncodeUtils.java
  class EncodeUtils (line 29) | public class EncodeUtils {
    method EncodeUtils (line 32) | private EncodeUtils() {
    method decode (line 46) | public static String decode(ByteBuffer bytes, int length, GPFlags flag...
    method decode (line 69) | public static String decode(byte[] data, GPFlags flags) {
    method decode (line 80) | private static String decode(byte[] data, Charset charset) {
    method encode (line 105) | public static byte[] encode(String name, GPFlags flags) {
    method flagsCharset (line 119) | private static Charset flagsCharset(GPFlags flags) {
    method canAsciiEncode (line 133) | public static boolean canAsciiEncode(String text) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Eocd.java
  class Eocd (line 30) | class Eocd {
    method Eocd (line 130) | Eocd(ByteBuffer bytes) throws IOException {
    method Eocd (line 181) | Eocd(long totalRecords, long directoryOffset, long directorySize, byte...
    method getTotalRecords (line 198) | long getTotalRecords() {
    method getDirectoryOffset (line 208) | long getDirectoryOffset() {
    method getDirectorySize (line 217) | long getDirectorySize() {
    method getEocdSize (line 226) | long getEocdSize() {
    method toBytes (line 236) | byte[] toBytes() throws IOException {
    method getComment (line 246) | byte[] getComment() {
    method computeByteRepresentation (line 258) | private byte[] computeByteRepresentation() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/EocdGroup.java
  class EocdGroup (line 34) | public class EocdGroup {
    method EocdGroup (line 132) | EocdGroup(ZFile file, FileUseMap map) {
    method readRecord (line 153) | void readRecord(long fileLength) throws IOException {
    method computeRecord (line 308) | void computeRecord(
    method appendToFile (line 380) | void appendToFile() throws IOException {
    method getEocdBytes (line 409) | byte[] getEocdBytes() throws IOException {
    method getEocdLocatorBytes (line 426) | @VisibleForTesting
    method getZ64EocdBytes (line 446) | @VisibleForTesting
    method isEmpty (line 464) | boolean isEmpty() {
    method setUseVersion2Header (line 476) | void setUseVersion2Header(boolean useVersion2Header) {
    method usingVersion2Header (line 489) | boolean usingVersion2Header() {
    method deleteRecord (line 496) | void deleteRecord() {
    method setEocdComment (line 526) | void setEocdComment(byte[] comment) {
    method getOffset (line 570) | long getOffset() {
    method getEocdComment (line 582) | byte[] getEocdComment() {
    method getDirectorySize (line 600) | long getDirectorySize() {
    method getDirectoryOffset (line 618) | long getDirectoryOffset() {
    method getTotalDirectoryRecords (line 636) | long getTotalDirectoryRecords() {
    method getRecordStart (line 654) | long getRecordStart() {
    method getRecordSize (line 669) | public long getRecordSize() {
    method getExtensibleData (line 687) | @Nullable

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ExtraField.java
  class ExtraField (line 48) | public class ExtraField {
    method ExtraField (line 75) | public ExtraField(byte[] rawData) {
    method ExtraField (line 81) | public ExtraField() {
    method ExtraField (line 91) | public ExtraField(ImmutableList<Segment> segments) {
    method getSegments (line 102) | public ImmutableList<Segment> getSegments() throws IOException {
    method getSingleSegment (line 118) | @Nullable
    method parseSegments (line 141) | private void parseSegments() throws IOException {
    method size (line 185) | public int size() {
    method write (line 205) | public void write(ByteBuffer out) throws IOException {
    method identifySegmentFactory (line 222) | private static SegmentFactory identifySegmentFactory(int headerId) {
    type Segment (line 234) | public interface Segment {
      method getHeaderId (line 241) | int getHeaderId();
      method size (line 248) | int size();
      method write (line 257) | void write(ByteBuffer out) throws IOException;
    type SegmentFactory (line 261) | interface SegmentFactory {
      method make (line 271) | Segment make(int headerId, byte[] data) throws IOException;
    class RawDataSegment (line 278) | public static class RawDataSegment implements Segment {
      method RawDataSegment (line 292) | RawDataSegment(int headerId, byte[] data) {
      method getHeaderId (line 297) | @Override
      method write (line 302) | @Override
      method size (line 309) | @Override
    class AlignmentSegment (line 322) | public static class AlignmentSegment implements Segment {
      method AlignmentSegment (line 339) | public AlignmentSegment(int alignment, int totalSize) {
      method AlignmentSegment (line 358) | public AlignmentSegment(int headerId, byte[] data) throws IOException {
      method write (line 370) | @Override
      method size (line 378) | @Override
      method getHeaderId (line 383) | @Override
    class LinkingEntrySegment (line 389) | public static class LinkingEntrySegment implements Segment {
      method LinkingEntrySegment (line 395) | public LinkingEntrySegment(StoredEntry linkingEntry) throws IOExcept...
      method getHeaderId (line 400) | @Override
      method size (line 405) | @Override
      method setOffset (line 410) | public void setOffset(int dataOffset, long zipOffset) {
      method write (line 415) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/FileUseMap.java
  class FileUseMap (line 49) | class FileUseMap {
    method FileUseMap (line 78) | FileUseMap(long size, int minFreeSize) {
    method internalAdd (line 98) | private void internalAdd(FileUseMapEntry<?> entry) {
    method internalRemove (line 112) | private void internalRemove(FileUseMapEntry<?> entry) {
    method add (line 129) | private void add(FileUseMapEntry<?> entry) {
    method add (line 158) | <T> FileUseMapEntry<T> add(long start, long end, T store) {
    method remove (line 173) | void remove(FileUseMapEntry<?> entry) {
    method findContainer (line 190) | private FileUseMapEntry<?> findContainer(FileUseMapEntry<?> entry) {
    method split (line 209) | private static Set<FileUseMapEntry<?>> split(
    method coalesce (line 242) | private void coalesce(FileUseMapEntry<?> entry) {
    method truncate (line 292) | void truncate() {
    method size (line 313) | long size() {
    method usedSize (line 322) | long usedSize() {
    method extend (line 347) | void extend(long size) {
    method locateFree (line 378) | long locateFree(long size, long alignOffset, long align, PositionAlgor...
    method getFreeAreas (line 507) | List<FileUseMapEntry<?>> getFreeAreas() {
    method before (line 526) | @Nullable
    method after (line 540) | @Nullable
    method at (line 554) | @Nullable
    method toString (line 570) | @Override
    type PositionAlgorithm (line 591) | public enum PositionAlgorithm {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/FileUseMapEntry.java
  class FileUseMapEntry (line 36) | class FileUseMapEntry<T> {
    method FileUseMapEntry (line 62) | private FileUseMapEntry(long start, long end, @Nullable T store) {
    method makeFree (line 78) | public static FileUseMapEntry<Object> makeFree(long start, long end) {
    method makeUsed (line 91) | public static <T> FileUseMapEntry<T> makeUsed(long start, long end, T ...
    method getStart (line 102) | long getStart() {
    method getEnd (line 111) | long getEnd() {
    method getSize (line 120) | long getSize() {
    method isFree (line 129) | boolean isFree() {
    method getStore (line 138) | @Nullable
    method toString (line 143) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/GPFlags.java
  class GPFlags (line 33) | class GPFlags {
    method GPFlags (line 73) | private GPFlags(long value) {
    method getValue (line 85) | public long getValue() {
    method isDeferredCrc (line 94) | public boolean isDeferredCrc() {
    method isUtf8FileName (line 103) | public boolean isUtf8FileName() {
    method make (line 113) | static GPFlags make(boolean utf8Encoding) {
    method from (line 131) | static GPFlags from(long bits) throws IOException {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/InflaterByteSource.java
  class InflaterByteSource (line 31) | public class InflaterByteSource extends CloseableByteSource {
    method InflaterByteSource (line 41) | public InflaterByteSource(CloseableByteSource byteSource) {
    method openStream (line 45) | @Override
    method innerClose (line 57) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/LazyDelegateByteSource.java
  class LazyDelegateByteSource (line 37) | public class LazyDelegateByteSource extends CloseableByteSource {
    method LazyDelegateByteSource (line 47) | public LazyDelegateByteSource(ListenableFuture<CloseableByteSource> de...
    method getDelegate (line 56) | public ListenableFuture<CloseableByteSource> getDelegate() {
    method get (line 66) | private CloseableByteSource get() throws IOException {
    method asCharSource (line 81) | @Override
    method openBufferedStream (line 90) | @Override
    method slice (line 95) | @Override
    method isEmpty (line 104) | @Override
    method size (line 109) | @Override
    method copyTo (line 114) | @Override
    method copyTo (line 119) | @Override
    method read (line 124) | @Override
    method read (line 129) | @Override
    method hash (line 134) | @Override
    method contentEquals (line 139) | @Override
    method openStream (line 144) | @Override
    method innerClose (line 149) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/NestedZip.java
  class NestedZip (line 9) | public class NestedZip extends ZFile {
    type NameCallback (line 12) | public interface NameCallback {
      method getName (line 13) | String getName(ZFile file) throws IOException;
    method NestedZip (line 16) | public NestedZip(NameCallback name, ZFile target, File src, boolean ma...
    method addFileLink (line 25) | public boolean addFileLink(StoredEntry srcEntry, String dstName) throw...
    method addFileLink (line 39) | public boolean addFileLink(String srcName, String dstName) throws IOEx...
    method getTarget (line 44) | public ZFile getTarget() {
    method getEntry (line 48) | public StoredEntry getEntry() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ProcessedAndRawByteSources.java
  class ProcessedAndRawByteSources (line 30) | public class ProcessedAndRawByteSources implements Closeable {
    method ProcessedAndRawByteSources (line 44) | public ProcessedAndRawByteSources(
    method getProcessedByteSource (line 55) | public CloseableByteSource getProcessedByteSource() {
    method getRawByteSource (line 66) | public CloseableByteSource getRawByteSource() {
    method close (line 70) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/StoredEntry.java
  class StoredEntry (line 52) | public class StoredEntry {
    method StoredEntry (line 176) | StoredEntry(
    method StoredEntry (line 185) | StoredEntry(
    method linkingCentralDirectoryHeader (line 198) | private CentralDirectoryHeader linkingCentralDirectoryHeader(String na...
    method StoredEntry (line 204) | private StoredEntry(
    method getLocalHeaderSize (line 305) | public int getLocalHeaderSize() {
    method getInFileSize (line 317) | long getInFileSize() throws IOException {
    method open (line 330) | public InputStream open() throws IOException {
    method read (line 340) | public byte[] read() throws IOException {
    method read (line 353) | public int read(byte[] bytes) throws IOException {
    method getType (line 368) | public StoredEntryType getType() {
    method delete (line 380) | public void delete() throws IOException {
    method delete (line 393) | void delete(boolean notify) throws IOException {
    method isDeleted (line 401) | public boolean isDeleted() {
    method getCentralDirectoryHeader (line 410) | public CentralDirectoryHeader getCentralDirectoryHeader() {
    method readLocalHeader (line 424) | private void readLocalHeader() throws IOException {
    method readDataDescriptorRecord (line 490) | private DataDescriptorType readDataDescriptorRecord() throws IOExcepti...
    method createSourceFromZip (line 533) | private ProcessedAndRawByteSources createSourceFromZip(final long zipO...
    method createSourcesFromRawContents (line 586) | private ProcessedAndRawByteSources createSourcesFromRawContents(Closea...
    method replaceSourceFromZip (line 619) | void replaceSourceFromZip(long zipFileOffset) throws IOException {
    method loadSourceIntoMemory (line 637) | void loadSourceIntoMemory() throws IOException {
    method getSource (line 666) | ProcessedAndRawByteSources getSource() {
    method getDataDescriptorType (line 675) | public DataDescriptorType getDataDescriptorType() {
    method removeDataDescriptor (line 685) | boolean removeDataDescriptor() {
    method toHeaderData (line 702) | int toHeaderData(byte[] buffer) throws IOException {
    method writeData (line 713) | private void writeData(ByteBuffer out) throws IOException {
    method writeData (line 717) | void writeData(ByteBuffer out, int extraOffset) throws IOException {
    method realign (line 755) | public boolean realign() throws IOException {
    method isLinkingEntry (line 763) | public boolean isLinkingEntry() {
    method isDummyEntry (line 767) | public boolean isDummyEntry() {
    method getNestedOffset (line 771) | public long getNestedOffset() {
    method getLocalExtra (line 780) | public ExtraField getLocalExtra() {
    method setLocalExtra (line 790) | public void setLocalExtra(ExtraField localExtra) throws IOException {
    method setLocalExtraNoNotify (line 804) | boolean setLocalExtraNoNotify(ExtraField localExtra) throws IOException {
    method getVerifyLog (line 835) | public VerifyLog getVerifyLog() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/StoredEntryType.java
  type StoredEntryType (line 20) | public enum StoredEntryType {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/VerifyLog.java
  type VerifyLog (line 25) | public interface VerifyLog {
    method log (line 32) | void log(String message);
    method getLogs (line 39) | ImmutableList<String> getLogs();
    method verify (line 49) | default void verify(boolean condition, String message, Object... args) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/VerifyLogs.java
  class VerifyLogs (line 24) | final class VerifyLogs {
    method VerifyLogs (line 26) | private VerifyLogs() {}
    method devNull (line 33) | static VerifyLog devNull() {
    method unlimited (line 50) | static VerifyLog unlimited() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFile.java
  class ZFile (line 187) | public class ZFile implements Closeable {
    method ZFile (line 384) | @Deprecated
    method ZFile (line 401) | @Deprecated
    method ZFile (line 420) | @Deprecated
    method openReadOnly (line 498) | @Deprecated
    method openReadOnly (line 510) | public static ZFile openReadOnly(File file) throws IOException {
    method openReadOnly (line 523) | public static ZFile openReadOnly(File file, ZFileOptions options) thro...
    method openReadWrite (line 540) | public static ZFile openReadWrite(File file) throws IOException {
    method openReadWrite (line 558) | public static ZFile openReadWrite(File file, ZFileOptions options) thr...
    method getSkipValidation (line 562) | public boolean getSkipValidation() {
    method entries (line 572) | public Set<StoredEntry> entries() {
    method get (line 602) | @Nullable
    method readData (line 628) | private void readData() throws IOException {
    method readEocd (line 740) | private void readEocd() throws IOException {
    method readCentralDirectory (line 853) | private void readCentralDirectory() throws IOException {
    method directOpen (line 917) | public InputStream directOpen(final long start, final long end) throws...
    method delete (line 982) | void delete(final StoredEntry entry, boolean notify) throws IOException {
    method checkNotInReadOnlyMode (line 1005) | private void checkNotInReadOnlyMode() {
    method update (line 1018) | public void update() throws IOException {
    method writeAllFilesToZip (line 1066) | private void writeAllFilesToZip() throws IOException {
    method recomputeAndWriteCentralDirectoryAndEocd (line 1223) | private void recomputeAndWriteCentralDirectoryAndEocd() throws IOExcep...
    method packIfNecessary (line 1276) | private void packIfNecessary() throws IOException {
    method reAdd (line 1314) | private void reAdd(StoredEntry entry, PositionHint positionHint) throw...
    method localHeaderChanged (line 1337) | void localHeaderChanged(StoredEntry entry, boolean resized) throws IOE...
    method centralDirectoryChanged (line 1346) | void centralDirectoryChanged() {
    method close (line 1352) | @Override
    method deleteDirectoryAndEocd (line 1377) | private void deleteDirectoryAndEocd() {
    method writeEntry (line 1402) | private void writeEntry(StoredEntry entry, long offset, byte[] chunk) ...
    method computeCentralDirectory (line 1444) | private void computeCentralDirectory() throws IOException {
    method appendCentralDirectory (line 1480) | private void appendCentralDirectory() throws IOException {
    method getCentralDirectoryBytes (line 1513) | public byte[] getCentralDirectoryBytes() throws IOException {
    method computeEocd (line 1533) | private void computeEocd() throws IOException {
    method appendEocd (line 1577) | private void appendEocd() throws IOException {
    method getEocdBytes (line 1598) | public byte[] getEocdBytes() throws IOException {
    method innerClose (line 1611) | private void innerClose() throws IOException {
    method openReadOnlyIfClosed (line 1636) | public void openReadOnlyIfClosed() throws IOException {
    method reopenRw (line 1652) | private void reopenRw() throws IOException {
    method add (line 1709) | public void add(String name, InputStream stream) throws IOException {
    method add (line 1730) | public StoredEntry add(String name, InputStream stream, boolean mayCom...
    method add (line 1750) | public void add(String name, ByteSource source, boolean mayCompress) t...
    method add (line 1758) | private StoredEntry add(String name, CloseableByteSource source, boole...
    method addLink (line 1770) | public void addLink(StoredEntry linkedEntry, String dstName)
    method addNestedLink (line 1775) | void addNestedLink(StoredEntry linkedEntry, String dstName, StoredEntr...
    method addNestedZip (line 1786) | public NestedZip addNestedZip(NestedZip.NameCallback name, File src, b...
    method add (line 1804) | private StoredEntry add(final StoredEntry newEntry) throws IOException {
    method makeStoredEntry (line 1820) | private StoredEntry makeStoredEntry(String name, CloseableByteSource s...
    method createSources (line 1855) | private ProcessedAndRawByteSources createSources(
    method processAllReadyEntries (line 1900) | private void processAllReadyEntries() throws IOException {
    method processAllReadyEntriesWithWait (line 1945) | private void processAllReadyEntriesWithWait() throws IOException {
    method addToEntries (line 1967) | private void addToEntries(final StoredEntry newEntry) throws IOExcepti...
    method positionInFile (line 2012) | private FileUseMapEntry<StoredEntry> positionInFile(StoredEntry entry,...
    method chooseAlignment (line 2049) | private int chooseAlignment(StoredEntry entry) throws IOException {
    method mergeFrom (line 2076) | public void mergeFrom(ZFile src, Predicate<String> ignoreFilter) throw...
    method touch (line 2170) | public void touch() {
    method finishAllBackgroundTasks (line 2184) | public void finishAllBackgroundTasks() throws IOException {
    method realign (line 2200) | public boolean realign() throws IOException {
    method realign (line 2224) | boolean realign(StoredEntry entry) throws IOException {
    method addZFileExtension (line 2316) | public void addZFileExtension(ZFileExtension extension) {
    method removeZFileExtension (line 2327) | public void removeZFileExtension(ZFileExtension extension) {
    method notify (line 2339) | private void notify(IOExceptionFunction<ZFileExtension, IOExceptionRun...
    method directWrite (line 2373) | public void directWrite(long offset, byte[] data, int start, int count...
    method directWrite (line 2402) | public void directWrite(long offset, byte[] data) throws IOException {
    method directSize (line 2411) | public long directSize() throws IOException {
    method directRead (line 2434) | public int directRead(long offset, byte[] data, int start, int count) ...
    method directRead (line 2452) | public int directRead(long offset, ByteBuffer dest) throws IOException {
    method directRead (line 2478) | public int directRead(long offset, byte[] data) throws IOException {
    method directFullyRead (line 2490) | public void directFullyRead(long offset, byte[] data) throws IOExcepti...
    method directFullyRead (line 2502) | public void directFullyRead(long offset, ByteBuffer dest) throws IOExc...
    method addAllRecursively (line 2539) | public void addAllRecursively(File file) throws IOException {
    method addAllRecursively (line 2553) | public void addAllRecursively(File file, Predicate<? super File> mayCo...
    method addAllRecursively (line 2569) | private void addAllRecursively(File file, File base, Predicate<? super...
    method getCentralDirectoryOffset (line 2618) | public long getCentralDirectoryOffset() {
    method getCentralDirectorySize (line 2643) | public long getCentralDirectorySize() {
    method getEocdOffset (line 2660) | public long getEocdOffset() {
    method getEocdSize (line 2673) | public long getEocdSize() {
    method getEocdComment (line 2686) | public byte[] getEocdComment() {
    method setEocdComment (line 2706) | public void setEocdComment(byte[] comment) {
    method setExtraDirectoryOffset (line 2752) | public void setExtraDirectoryOffset(long offset) {
    method getExtraDirectoryOffset (line 2768) | public long getExtraDirectoryOffset() {
    method areTimestampsIgnored (line 2777) | public boolean areTimestampsIgnored() {
    method sortZipContents (line 2790) | public void sortZipContents() throws IOException {
    method getFile (line 2825) | public File getFile() {
    method asDataSource (line 2829) | public DataSource asDataSource() throws IOException {
    method asDataSource (line 2837) | public DataSource asDataSource(long offset, long size) throws IOExcept...
    method makeVerifyLog (line 2850) | VerifyLog makeVerifyLog() {
    method getVerifyLog (line 2861) | VerifyLog getVerifyLog() {
    method hasPendingChangesWithWait (line 2870) | public boolean hasPendingChangesWithWait() throws IOException {
    method getStorage (line 2881) | public ByteStorage getStorage() {
    type PositionHint (line 2886) | enum PositionHint {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFileExtension.java
  class ZFileExtension (line 56) | public abstract class ZFileExtension {
    method open (line 65) | @Nullable
    method beforeUpdate (line 83) | @Nullable
    method entriesWritten (line 99) | public void entriesWritten() throws IOException {}
    method updated (line 106) | public void updated() throws IOException {}
    method closed (line 113) | public void closed() {}
    method added (line 123) | @Nullable
    method removed (line 136) | @Nullable

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFileOptions.java
  class ZFileOptions (line 29) | public class ZFileOptions {
    method ZFileOptions (line 68) | public ZFileOptions() {
    method getStorageFactory (line 89) | public ByteStorageFactory getStorageFactory() {
    method getTracker (line 93) | @Deprecated
    method setStorageFactory (line 104) | public ZFileOptions setStorageFactory(ByteStorageFactory storage) {
    method getCompressor (line 114) | public Compressor getCompressor() {
    method setCompressor (line 123) | public ZFileOptions setCompressor(Compressor compressor) {
    method getNoTimestamps (line 133) | public boolean getNoTimestamps() {
    method setNoTimestamps (line 142) | public ZFileOptions setNoTimestamps(boolean noTimestamps) {
    method getAlignmentRule (line 152) | public AlignmentRule getAlignmentRule() {
    method setAlignmentRule (line 161) | public ZFileOptions setAlignmentRule(AlignmentRule alignmentRule) {
    method getCoverEmptySpaceUsingExtraField (line 172) | public boolean getCoverEmptySpaceUsingExtraField() {
    method setCoverEmptySpaceUsingExtraField (line 182) | public ZFileOptions setCoverEmptySpaceUsingExtraField(boolean coverEmp...
    method getAutoSortFiles (line 193) | public boolean getAutoSortFiles() {
    method setAutoSortFiles (line 203) | public ZFileOptions setAutoSortFiles(boolean autoSortFiles) {
    method setVerifyLogFactory (line 213) | public ZFileOptions setVerifyLogFactory(Supplier<VerifyLog> verifyLogF...
    method getVerifyLogFactory (line 224) | public Supplier<VerifyLog> getVerifyLogFactory() {
    method setSkipValidation (line 233) | public ZFileOptions setSkipValidation(boolean skipValidation) {
    method getSkipValidation (line 243) | public boolean getSkipValidation() {
    method setAlwaysGenerateJarManifest (line 251) | public ZFileOptions setAlwaysGenerateJarManifest(boolean alwaysGenerat...
    method getAlwaysGenerateJarManifest (line 257) | public boolean getAlwaysGenerateJarManifest() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Zip64Eocd.java
  class Zip64Eocd (line 28) | public class Zip64Eocd {
    method Zip64Eocd (line 234) | Zip64Eocd(
    method Zip64Eocd (line 260) | Zip64Eocd(ByteBuffer bytes) throws IOException {
    method sizeOfFixedFields (line 329) | private int sizeOfFixedFields() {
    method size (line 341) | public int size() {
    method getTotalRecords (line 345) | public long getTotalRecords() {
    method getDirectorySize (line 349) | public long getDirectorySize() {
    method getDirectoryOffset (line 353) | public long getDirectoryOffset() {
    method getExtraFields (line 357) | public Zip64ExtensibleDataSector getExtraFields() {
    method getVersionToExtract (line 361) | public long getVersionToExtract() { return versionToExtract; }
    method toBytes (line 368) | public byte[] toBytes() {
    method computeByteRepresentation (line 372) | private byte[] computeByteRepresentation() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Zip64EocdLocator.java
  class Zip64EocdLocator (line 31) | class Zip64EocdLocator {
    method Zip64EocdLocator (line 82) | Zip64EocdLocator(ByteBuffer bytes) throws IOException {
    method Zip64EocdLocator (line 99) | Zip64EocdLocator(long z64EocdOffset) {
    method getZ64EocdOffset (line 111) | long getZ64EocdOffset() {
    method getSize (line 120) | long getSize() {
    method toBytes (line 130) | byte[] toBytes() throws IOException {
    method computeByteRepresentation (line 141) | private byte[] computeByteRepresentation() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Zip64ExtensibleDataSector.java
  class Zip64ExtensibleDataSector (line 39) | public class Zip64ExtensibleDataSector {
    method Zip64ExtensibleDataSector (line 59) | public Zip64ExtensibleDataSector(byte[] rawData) {
    method Zip64ExtensibleDataSector (line 67) | public Zip64ExtensibleDataSector() {
    method Zip64ExtensibleDataSector (line 77) | public Zip64ExtensibleDataSector(ImmutableList<Z64SpecialPurposeData> ...
    method size (line 82) | int size() {
    method write (line 95) | void write(ByteBuffer out) throws IOException {
    method getFields (line 106) | public ImmutableList<Z64SpecialPurposeData> getFields() throws IOExcep...
    method parseData (line 115) | private void parseData() throws IOException {
    type Z64SpecialPurposeData (line 143) | public interface Z64SpecialPurposeData {
      method getHeaderId (line 153) | int getHeaderId();
      method size (line 160) | int size();
      method write (line 169) | void write(ByteBuffer out) throws IOException;
    type SpecialPurposeDataFactory (line 172) | public  interface SpecialPurposeDataFactory {
      method make (line 182) | Z64SpecialPurposeData make(int headerId, byte[] data) throws IOExcep...
    class RawSpecialPurposeData (line 189) | public static class RawSpecialPurposeData implements Z64SpecialPurpose...
      method RawSpecialPurposeData (line 197) | RawSpecialPurposeData(int headerId, byte[] data) {
      method getHeaderId (line 202) | @Override
      method size (line 207) | @Override
      method write (line 212) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipField.java
  class ZipField (line 61) | abstract class ZipField {
    method ZipField (line 86) | ZipField(int offset, int size, String name, ZipFieldInvariant... invar...
    method ZipField (line 107) | ZipField(int offset, int size, long expected, String name) {
    method checkVerifiesInvariants (line 127) | private void checkVerifiesInvariants(long value) throws IOException {
    method skip (line 150) | void skip(ByteBuffer bytes) throws IOException {
    method read (line 171) | long read(ByteBuffer bytes) throws IOException {
    method verify (line 204) | void verify(ByteBuffer bytes) throws IOException {
    method verify (line 218) | void verify(ByteBuffer bytes, @Nullable VerifyLog verifyLog) throws IO...
    method verify (line 232) | void verify(ByteBuffer bytes, long expected) throws IOException {
    method verify (line 247) | void verify(ByteBuffer bytes, long expected, @Nullable VerifyLog verif...
    method write (line 271) | void write(ByteBuffer output, long value) throws IOException {
    method write (line 296) | void write(ByteBuffer output) throws IOException {
    method offset (line 306) | int offset() {
    method endOffset (line 316) | int endOffset() {
    class F2 (line 321) | static class F2 extends ZipField {
      method F2 (line 330) | F2(int offset, String name, ZipFieldInvariant... invariants) {
      method F2 (line 341) | F2(int offset, long expected, String name) {
    class F4 (line 347) | static class F4 extends ZipField {
      method F4 (line 355) | F4(int offset, String name, ZipFieldInvariant... invariants) {
      method F4 (line 366) | F4(int offset, long expected, String name) {
    class F8 (line 372) | static class F8 extends ZipField {
      method F8 (line 381) | F8(int offset, String name, ZipFieldInvariant... invariants) {
      method F8 (line 392) | F8(int offset, long expected, String name) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariant.java
  type ZipFieldInvariant (line 23) | interface ZipFieldInvariant {
    method isValid (line 31) | boolean isValid(long value);
    method getName (line 38) | String getName();

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariantMaxValue.java
  class ZipFieldInvariantMaxValue (line 20) | class ZipFieldInvariantMaxValue implements ZipFieldInvariant {
    method ZipFieldInvariantMaxValue (line 30) | ZipFieldInvariantMaxValue(long max) {
    method isValid (line 34) | @Override
    method getName (line 39) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariantMinValue.java
  class ZipFieldInvariantMinValue (line 20) | class ZipFieldInvariantMinValue implements ZipFieldInvariant {
    method ZipFieldInvariantMinValue (line 30) | ZipFieldInvariantMinValue(long min) {
    method isValid (line 34) | @Override
    method getName (line 39) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariantNonNegative.java
  class ZipFieldInvariantNonNegative (line 20) | class ZipFieldInvariantNonNegative implements ZipFieldInvariant {
    method isValid (line 22) | @Override
    method getName (line 27) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFileState.java
  type ZipFileState (line 20) | enum ZipFileState {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/BestAndDefaultDeflateExecutorCompressor.java
  class BestAndDefaultDeflateExecutorCompressor (line 31) | public class BestAndDefaultDeflateExecutorCompressor extends ExecutorCom...
    method BestAndDefaultDeflateExecutorCompressor (line 54) | public BestAndDefaultDeflateExecutorCompressor(Executor executor, doub...
    method BestAndDefaultDeflateExecutorCompressor (line 65) | @Deprecated
    method immediateCompress (line 71) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/DeflateExecutionCompressor.java
  class DeflateExecutionCompressor (line 32) | public class DeflateExecutionCompressor extends ExecutorCompressor {
    method DeflateExecutionCompressor (line 43) | public DeflateExecutionCompressor(Executor executor, int level) {
    method DeflateExecutionCompressor (line 49) | @Deprecated
    method immediateCompress (line 54) | @Override

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/ExecutorCompressor.java
  class ExecutorCompressor (line 31) | public abstract class ExecutorCompressor implements Compressor {
    method ExecutorCompressor (line 41) | public ExecutorCompressor(Executor executor) {
    method compress (line 45) | @Override
    method immediateCompress (line 69) | protected abstract CompressionResult immediateCompress(

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/Zip64NotSupportedException.java
  class Zip64NotSupportedException (line 22) | public class Zip64NotSupportedException extends IOException {
    method Zip64NotSupportedException (line 24) | public Zip64NotSupportedException(String message) {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/ByteTracker.java
  class ByteTracker (line 25) | @Deprecated

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/CloseableByteSource.java
  class CloseableByteSource (line 29) | public abstract class CloseableByteSource extends ByteSource implements ...
    method CloseableByteSource (line 35) | public CloseableByteSource() {
    method close (line 39) | @Override
    method innerClose (line 58) | protected abstract void innerClose() throws IOException;

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/CloseableDelegateByteSource.java
  class CloseableDelegateByteSource (line 32) | public class CloseableDelegateByteSource extends CloseableByteSource {
    method CloseableDelegateByteSource (line 50) | public CloseableDelegateByteSource(ByteSource inner, long size) {
    method get (line 61) | private synchronized ByteSource get() {
    method innerClose (line 70) | @Override
    method sizeNoException (line 85) | public long sizeNoException() {
    method asCharSource (line 89) | @Override
    method openBufferedStream (line 94) | @Override
    method slice (line 99) | @Override
    method isEmpty (line 104) | @Override
    method size (line 109) | @Override
    method copyTo (line 114) | @Override
    method copyTo (line 119) | @Override
    method read (line 124) | @Override
    method read (line 129) | @Override
    method hash (line 134) | @Override
    method contentEquals (line 139) | @Override
    method openStream (line 144) | @Override
    class ByteSourceDisposedException (line 150) | private static class ByteSourceDisposedException extends RuntimeExcept...
      method ByteSourceDisposedException (line 153) | private ByteSourceDisposedException() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/LittleEndianUtils.java
  class LittleEndianUtils (line 32) | public class LittleEndianUtils {
    method LittleEndianUtils (line 34) | private LittleEndianUtils() {}
    method readUnsigned8Le (line 43) | public static long readUnsigned8Le(ByteBuffer bytes) throws IOException {
    method readUnsigned4Le (line 65) | public static long readUnsigned4Le(ByteBuffer bytes) throws IOException {
    method readUnsigned2Le (line 90) | public static int readUnsigned2Le(ByteBuffer bytes) throws IOException {
    method writeUnsigned8Le (line 114) | public static void writeUnsigned8Le(ByteBuffer output, long value) thr...
    method writeUnsigned4Le (line 130) | public static void writeUnsigned4Le(ByteBuffer output, long value) thr...
    method writeUnsigned2Le (line 149) | public static void writeUnsigned2Le(ByteBuffer output, int value) thro...

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/MsDosDateTimeUtils.java
  class MsDosDateTimeUtils (line 24) | public class MsDosDateTimeUtils {
    method MsDosDateTimeUtils (line 26) | private MsDosDateTimeUtils() {}
    method packTime (line 34) | public static int packTime(long time) {
    method packCurrentTime (line 58) | public static int packCurrentTime() {
    method packDate (line 68) | public static int packDate(long time) {
    method packCurrentDate (line 102) | public static int packCurrentDate() {

FILE: apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/RandomAccessFileUtils.java
  class RandomAccessFileUtils (line 23) | public final class RandomAccessFileUtils {
    method RandomAccessFileUtils (line 25) | private RandomAccessFileUtils() {}
    method fullyRead (line 35) | public static void fullyRead(RandomAccessFile raf, byte[] data) throws...

FILE: meta-loader/src/main/java/org/lsposed/lspatch/metaloader/LSPAppComponentFactoryStub.java
  class LSPAppComponentFactoryStub (line 28) | @SuppressLint("UnsafeDynamicallyLoadedCode")
    method transfer (line 101) | private static void transfer(InputStream is, OutputStream os) throws I...

FILE: patch-loader/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java
  class LSPApplication (line 50) | @SuppressWarnings("unused")
    method isIsolated (line 63) | public static boolean isIsolated() {
    method onLoad (line 67) | public static void onLoad() throws RemoteException, IOException {
    method createLoadedApkWithContext (line 102) | private static Context createLoadedApkWithContext() {
    method disableProfile (line 183) | public static void disableProfile(Context context) {
    method switchAllClassLoader (line 230) | private static void switchAllClassLoader() {

FILE: patch-loader/src/main/java/org/lsposed/lspatch/loader/LSPLoader.java
  class LSPLoader (line 11) | public class LSPLoader {
    method initModules (line 12) | public static void initModules(LoadedApk loadedApk) {

FILE: patch-loader/src/main/java/org/lsposed/lspatch/loader/SigBypass.java
  class SigBypass (line 32) | public class SigBypass {
    method replaceSignature (line 37) | private static void replaceSignature(Context context, PackageInfo pack...
    method hookPackageParser (line 72) | private static void hookPackageParser(Context context) {
    method proxyPackageInfoCreator (line 83) | private static void proxyPackageInfoCreator(Context context) {
    method doSigBypass (line 115) | static void doSigBypass(Context context, int sigBypassLevel) throws IO...

FILE: patch-loader/src/main/java/org/lsposed/lspatch/loader/util/FileUtils.java
  class FileUtils (line 10) | public class FileUtils {
    method deleteFolderIfExists (line 12) | public static void deleteFolderIfExists(Path target) throws IOException {

FILE: patch-loader/src/main/java/org/lsposed/lspatch/loader/util/XLog.java
  class XLog (line 5) | public class XLog {
    method d (line 9) | public static void d(String tag, String msg) {
    method v (line 15) | public static void v(String tag, String msg) {
    method w (line 21) | public static void w(String tag, String msg) {
    method i (line 27) | public static void i(String tag, String msg) {
    method e (line 33) | public static void e(String tag, String msg) {
    method e (line 39) | public static void e(String tag, String msg, Throwable tr) {

FILE: patch-loader/src/main/java/org/lsposed/lspatch/service/LocalApplicationService.java
  class LocalApplicationService (line 24) | public class LocalApplicationService extends ILSPApplicationService.Stub {
    method LocalApplicationService (line 30) | public LocalApplicationService(Context context) {
    method getLegacyModulesList (line 60) | @Override
    method getModulesList (line 65) | @Override
    method getPrefsPath (line 70) | @Override
    method requestInjectedManagerBinder (line 75) | @Override
    method asBinder (line 80) | @Override

FILE: patch-loader/src/main/java/org/lsposed/lspatch/service/RemoteApplicationService.java
  class RemoteApplicationService (line 32) | public class RemoteApplicationService implements ILSPApplicationService {
    method RemoteApplicationService (line 39) | @SuppressLint("DiscouragedPrivateApi")
    method getLegacyModulesList (line 86) | @Override
    method getModulesList (line 91) | @Override
    method getPrefsPath (line 96) | @Override
    method asBinder (line 101) | @Override
    method requestInjectedManagerBinder (line 106) | @Override

FILE: patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SigBypass.java
  class SigBypass (line 3) | public class SigBypass {
    method enableOpenatHook (line 4) | public static native void enableOpenatHook(String origApkPath, String ...

FILE: patch-loader/src/main/jni/api/patch_main.cpp
  function JNIEXPORT (line 29) | JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {

FILE: patch-loader/src/main/jni/include/art/runtime/jit/profile_saver.h
  function namespace (line 12) | namespace art {

FILE: patch-loader/src/main/jni/include/art/runtime/oat_file_manager.h
  function namespace (line 28) | namespace art {

FILE: patch-loader/src/main/jni/src/config_impl.h
  function namespace (line 29) | namespace lspd {

FILE: patch-loader/src/main/jni/src/jni/bypass_sig.cpp
  type lspd (line 13) | namespace lspd {
    function LSP_DEF_NATIVE_METHOD (line 29) | LSP_DEF_NATIVE_METHOD(void, SigBypass, enableOpenatHook, jstring origA...
    function RegisterBypass (line 48) | void RegisterBypass(JNIEnv* env) {

FILE: patch-loader/src/main/jni/src/jni/bypass_sig.h
  function namespace (line 5) | namespace lspd {

FILE: patch-loader/src/main/jni/src/patch_loader.cpp
  type lspd (line 35) | namespace lspd {

FILE: patch-loader/src/main/jni/src/patch_loader.h
  function namespace (line 28) | namespace lspd {

FILE: patch/src/main/java/org/lsposed/patch/LSPatch.java
  class LSPatch (line 51) | public class LSPatch {
    class PatchError (line 53) | static class PatchError extends Error {
      method PatchError (line 54) | public PatchError(String message, Throwable cause) {
      method PatchError (line 58) | PatchError(String message) {
    method LSPatch (line 113) | public LSPatch(Logger logger, String... args) {
    method main (line 134) | public static void main(String... args) throws IOException {
    method doCommandLine (line 147) | public void doCommandLine() throws PatchError, IOException {
    method patch (line 170) | public void patch(File srcApkFile, File outputFile) throws PatchError,...
    method embedModules (line 306) | private void embedModules(ZFile zFile) {
    method modifyManifestFile (line 323) | private byte[] modifyManifestFile(InputStream is, String metadata, int...

FILE: patch/src/main/java/org/lsposed/patch/util/ApkSignatureHelper.java
  class ApkSignatureHelper (line 18) | public class ApkSignatureHelper {
    method toChars (line 22) | private static char[] toChars(byte[] mSignature) {
    method loadCertificates (line 37) | private static Certificate[] loadCertificates(JarFile jarFile, JarEntr...
    method getApkSignInfo (line 49) | public static String getApkSignInfo(String apkFilePath) {
    method getApkSignV1 (line 57) | public static String getApkSignV1(String apkFilePath) {
    method getApkSignV2 (line 97) | private static String getApkSignV2(String apkFilePath) throws IOExcept...

FILE: patch/src/main/java/org/lsposed/patch/util/JavaLogger.java
  class JavaLogger (line 3) | public class JavaLogger extends Logger {
    method d (line 5) | @Override
    method i (line 10) | @Override
    method e (line 15) | @Override

FILE: patch/src/main/java/org/lsposed/patch/util/Logger.java
  class Logger (line 3) | public abstract class Logger {
    method d (line 7) | abstract public void d(String msg);
    method i (line 9) | abstract public void i(String msg);
    method e (line 11) | abstract public void e(String msg);

FILE: patch/src/main/java/org/lsposed/patch/util/ManifestParser.java
  class ManifestParser (line 15) | public class ManifestParser {
    method parseManifestFile (line 17) | public static Pair parseManifestFile(InputStream is) throws IOException {
    method parseManifestFile (line 74) | public static Pair parseManifestFile(String filePath) throws IOExcepti...
    class Pair (line 81) | public static class Pair {
      method Pair (line 87) | public Pair(String packageName, String appComponentFactory, int minS...

FILE: share/android/src/main/java/org/lsposed/lspatch/util/ModuleLoader.java
  class ModuleLoader (line 18) | public class ModuleLoader {
    method readDexes (line 22) | private static void readDexes(ZipFile apkFile, List<SharedMemory> preL...
    method readName (line 39) | private static void readName(ZipFile apkFile, String initName, List<St...
    method loadModule (line 55) | public static PreLoadedApk loadModule(String path) {

FILE: share/java/src/main/java/org/lsposed/lspatch/share/Constants.java
  class Constants (line 3) | public class Constants {

FILE: share/java/src/main/java/org/lsposed/lspatch/share/PatchConfig.java
  class PatchConfig (line 3) | public class PatchConfig {
    method PatchConfig (line 13) | public PatchConfig(

FILE: share/java/src/template/java/org.lsposed.lspatch.share/LSPConfig.java
  class LSPConfig (line 3) | public class LSPConfig {
    method LSPConfig (line 13) | private LSPConfig() {
Condensed preview — 260 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,167K chars).
[
  {
    "path": ".gitattributes",
    "chars": 297,
    "preview": "# Set the default behavior, in case people don't have core.autocrlf set.\n* text=auto eol=lf\n\n# Declare files that will a"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2652,
    "preview": "name: Bug report/反馈 Bug\ndescription: Report errors or unexpected behavior./反馈错误或异常行为。\nlabels: [bug]\ntitle: \"[Bug] \"\nbody"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 351,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question/提问\n    url: https://github.com/LSPosed/LSPatch/discu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 796,
    "preview": "---\nname: Feature request/新特性请求\ndescription: Suggest an idea./提出建议\nlabels: [enhancement]\ntitle: \"[Feature Request] \"\nbod"
  },
  {
    "path": ".github/workflows/crowdin.yml",
    "chars": 644,
    "preview": "name: Crowdin Action\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ master ]\n    paths:\n    - manager/src/main/res/va"
  },
  {
    "path": ".github/workflows/main.yml",
    "chars": 5459,
    "preview": "name: Android CI\n\non:\n  workflow_dispatch:\n  push:\n    branches: [ master ]\n  pull_request:\n\njobs:\n  build:\n    name: Bu"
  },
  {
    "path": ".gitignore",
    "chars": 220,
    "preview": "*.iml\n.gradle\n/local.properties\n/.idea/caches\n/.idea/libraries\n/.idea/modules.xml\n/.idea/workspace.xml\n/.idea/navEditor."
  },
  {
    "path": ".gitmodules",
    "chars": 361,
    "preview": "[submodule \"apksigner\"]\n\tpath = apksigner\n\turl = https://android.googlesource.com/platform/tools/apksig.git\n\tbranch = an"
  },
  {
    "path": "LICENSE",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "README.md",
    "chars": 1950,
    "preview": "# LSPatch Framework\n\n[![Build](https://img.shields.io/github/actions/workflow/status/LSPosed/LSPatch/main.yml?branch=mas"
  },
  {
    "path": "apkzlib/.gitignore",
    "chars": 7,
    "preview": "/build\n"
  },
  {
    "path": "apkzlib/build.gradle.kts",
    "chars": 712,
    "preview": "val androidSourceCompatibility: JavaVersion by rootProject.extra\nval androidTargetCompatibility: JavaVersion by rootProj"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/AbstractCloseableByteSourceFromOutputStreamBuilder.java",
    "chars": 2716,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ByteStorage.java",
    "chars": 2587,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ByteStorageFactory.java",
    "chars": 327,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport java.io.IOException;\n\n/** Factory that creates {@link ByteS"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedByteStorage.java",
    "chars": 4604,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedByteStorageFactory.java",
    "chars": 1277,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport java.io.IOException;\nimport javax.annotation.Nullable;\n\n/**"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/ChunkBasedCloseableByteSource.java",
    "chars": 1460,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/CloseableByteSourceFromOutputStreamBuilder.java",
    "chars": 957,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/InMemoryByteStorage.java",
    "chars": 3194,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/InMemoryByteStorageFactory.java",
    "chars": 377,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport java.io.IOException;\n\n/**\n * {@link ByteStorageFactory} tha"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LimitedInputStream.java",
    "chars": 2354,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LruTrackedCloseableByteSource.java",
    "chars": 2371,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/LruTracker.java",
    "chars": 2916,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.google.common.base.Preconditions;\nimport com.google.com"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/OverflowToDiskByteStorage.java",
    "chars": 6082,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/OverflowToDiskByteStorageFactory.java",
    "chars": 2065,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport java.io.IOException;\nimport javax.annotation.Nullable;\n\n/**"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/SwitchableDelegateCloseableByteSource.java",
    "chars": 4422,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/SwitchableDelegateInputStream.java",
    "chars": 4855,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport jav"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectory.java",
    "chars": 2535,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.google.common.annotations.VisibleForTesting;\nimport jav"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectoryFactory.java",
    "chars": 998,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Factory t"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryDirectoryStorage.java",
    "chars": 3337,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableByteSour"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryFile.java",
    "chars": 1644,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.google.common.base.Preconditions;\nimport java.io.Closea"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/bytestorage/TemporaryFileCloseableByteSource.java",
    "chars": 1246,
    "preview": "package com.android.tools.build.apkzlib.bytestorage;\n\nimport com.android.tools.build.apkzlib.zip.utils.CloseableDelegate"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/DigestAlgorithm.java",
    "chars": 2346,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/ManifestGenerationExtension.java",
    "chars": 8138,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SignatureAlgorithm.java",
    "chars": 3432,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SigningExtension.java",
    "chars": 17157,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/SigningOptions.java",
    "chars": 4062,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/sign/package-info.java",
    "chars": 9641,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/ApkZLibPair.java",
    "chars": 1036,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/CachedFileContents.java",
    "chars": 4385,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/CachedSupplier.java",
    "chars": 3022,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionConsumer.java",
    "chars": 977,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionFunction.java",
    "chars": 1413,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionRunnable.java",
    "chars": 1234,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/IOExceptionWrapper.java",
    "chars": 1211,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/SigningBlockUtils.java",
    "chars": 8129,
    "preview": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/utils/package-info.java",
    "chars": 715,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkCreator.java",
    "chars": 2435,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkCreatorFactory.java",
    "chars": 4336,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkZFileCreator.java",
    "chars": 5911,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ApkZFileCreatorFactory.java",
    "chars": 1459,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ManifestAttributes.java",
    "chars": 1164,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/NativeLibrariesPackagingMode.java",
    "chars": 1133,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/ZFiles.java",
    "chars": 5525,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zfile/package-info.java",
    "chars": 710,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/AlignmentRule.java",
    "chars": 1225,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/AlignmentRules.java",
    "chars": 2466,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CentralDirectory.java",
    "chars": 18942,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CentralDirectoryHeader.java",
    "chars": 11739,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CentralDirectoryHeaderCompressInfo.java",
    "chars": 3498,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CompressionMethod.java",
    "chars": 1683,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/CompressionResult.java",
    "chars": 1928,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Compressor.java",
    "chars": 1482,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/DataDescriptorType.java",
    "chars": 1764,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/EncodeUtils.java",
    "chars": 3847,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Eocd.java",
    "chars": 9881,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/EocdGroup.java",
    "chars": 25101,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ExtraField.java",
    "chars": 13139,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/FileUseMap.java",
    "chars": 18842,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/FileUseMapEntry.java",
    "chars": 4348,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/GPFlags.java",
    "chars": 4394,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/InflaterByteSource.java",
    "chars": 2034,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/LazyDelegateByteSource.java",
    "chars": 4250,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/NestedZip.java",
    "chars": 1791,
    "preview": "package com.android.tools.build.apkzlib.zip;\n\nimport com.google.common.collect.Maps;\n\nimport java.io.File;\nimport java.i"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ProcessedAndRawByteSources.java",
    "chars": 2488,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/StoredEntry.java",
    "chars": 30209,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/StoredEntryType.java",
    "chars": 806,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/VerifyLog.java",
    "chars": 1608,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/VerifyLogs.java",
    "chars": 1687,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFile.java",
    "chars": 109406,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFileExtension.java",
    "chars": 5933,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZFileOptions.java",
    "chars": 7945,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Zip64Eocd.java",
    "chars": 15443,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Zip64EocdLocator.java",
    "chars": 5470,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/Zip64ExtensibleDataSector.java",
    "chars": 6387,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipField.java",
    "chars": 13974,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariant.java",
    "chars": 1149,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariantMaxValue.java",
    "chars": 1191,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariantMinValue.java",
    "chars": 1189,
    "preview": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFieldInvariantNonNegative.java",
    "chars": 949,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/ZipFileState.java",
    "chars": 924,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/BestAndDefaultDeflateExecutorCompressor.java",
    "chars": 3275,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/DeflateExecutionCompressor.java",
    "chars": 2652,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/ExecutorCompressor.java",
    "chars": 2438,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/Zip64NotSupportedException.java",
    "chars": 935,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/compress/package-info.java",
    "chars": 731,
    "preview": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/ByteTracker.java",
    "chars": 826,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/CloseableByteSource.java",
    "chars": 1751,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/CloseableDelegateByteSource.java",
    "chars": 4278,
    "preview": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/LittleEndianUtils.java",
    "chars": 5662,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/MsDosDateTimeUtils.java",
    "chars": 3244,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "apkzlib/src/main/java/com/android/tools/build/apkzlib/zip/utils/RandomAccessFileUtils.java",
    "chars": 1651,
    "preview": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "build.gradle.kts",
    "chars": 8829,
    "preview": "import com.android.build.api.dsl.ApplicationExtension\nimport com.android.build.api.variant.ApplicationAndroidComponentsE"
  },
  {
    "path": "crowdin.yml",
    "chars": 622,
    "preview": "#\n# Your Crowdin credentials\n#\n\"project_id_env\": \"CROWDIN_PROJECT_ID\"\n\"api_token_env\": \"CROWDIN_API_TOKEN\"\n\"base_path\": "
  },
  {
    "path": "gradle/lspatch.versions.toml",
    "chars": 3066,
    "preview": "[versions]\nroom = \"2.5.2\"\naccompanist = \"0.27.0\"\ncompose-destinations = \"1.9.42-beta\"\nshizuku = \"13.1.2\"\nhiddenapi-refin"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 223,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradle.properties",
    "chars": 134,
    "preview": "android.experimental.enableNewResourceShrinker.preciseShrinking=true\nandroid.enableAppCompileTimeRClass=true\nandroid.use"
  },
  {
    "path": "gradlew",
    "chars": 8473,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "gradlew.bat",
    "chars": 2868,
    "preview": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (th"
  },
  {
    "path": "jar/.gitignore",
    "chars": 6,
    "preview": "/build"
  },
  {
    "path": "jar/build.gradle.kts",
    "chars": 1359,
    "preview": "val verCode: Int by rootProject.extra\nval verName: String by rootProject.extra\nval androidSourceCompatibility: JavaVersi"
  },
  {
    "path": "manager/.gitignore",
    "chars": 6,
    "preview": "/build"
  },
  {
    "path": "manager/build.gradle.kts",
    "chars": 4266,
    "preview": "import java.util.Locale\n\nval defaultManagerPackageName: String by rootProject.extra\nval apiCode: Int by rootProject.extr"
  },
  {
    "path": "manager/proguard-rules-debug.pro",
    "chars": 393,
    "preview": "-dontobfuscate\n-keep class com.beust.jcommander.** { *; }\n-keep class org.lsposed.lspatch.Patcher$Options { *; }\n-keep c"
  },
  {
    "path": "manager/proguard-rules.pro",
    "chars": 749,
    "preview": "-assumenosideeffects class kotlin.jvm.internal.Intrinsics {\n public static void check*(...);\n public static void throw*("
  },
  {
    "path": "manager/src/main/AndroidManifest.xml",
    "chars": 1846,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/LSPApplication.kt",
    "chars": 1114,
    "preview": "package org.lsposed.lspatch\n\nimport android.app.Application\nimport android.content.Context\nimport android.content.Shared"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/Patcher.kt",
    "chars": 2770,
    "preview": "package org.lsposed.lspatch\n\nimport androidx.core.net.toUri\nimport androidx.documentfile.provider.DocumentFile\nimport ko"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/config/ConfigManager.kt",
    "chars": 3412,
    "preview": "package org.lsposed.lspatch.config\n\nimport android.content.pm.PackageManager\nimport android.util.Log\nimport androidx.roo"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/config/Configs.kt",
    "chars": 1516,
    "preview": "package org.lsposed.lspatch.config\n\nimport org.lsposed.lspatch.lspApp\nimport org.lsposed.lspatch.ui.util.delegateStateOf"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/config/MyKeyStore.kt",
    "chars": 1144,
    "preview": "package org.lsposed.lspatch.config\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStat"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/database/LSPDatabase.kt",
    "chars": 493,
    "preview": "package org.lsposed.lspatch.database\n\nimport androidx.room.Database\nimport androidx.room.RoomDatabase\nimport org.lsposed"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/database/dao/ModuleDao.kt",
    "chars": 601,
    "preview": "package org.lsposed.lspatch.database.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Inse"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/database/dao/ScopeDao.kt",
    "chars": 575,
    "preview": "package org.lsposed.lspatch.database.dao\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Inse"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/database/entity/Module.kt",
    "chars": 196,
    "preview": "package org.lsposed.lspatch.database.entity\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\ndata c"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/database/entity/Scope.kt",
    "chars": 395,
    "preview": "package org.lsposed.lspatch.database.entity\n\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\n\n@Entity(\n    p"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/manager/AppBroadcastReceiver.kt",
    "chars": 1177,
    "preview": "package org.lsposed.lspatch.manager\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport andr"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/manager/ManagerService.kt",
    "chars": 1143,
    "preview": "package org.lsposed.lspatch.manager\n\nimport android.os.Binder\nimport android.os.Bundle\nimport android.os.IBinder\nimport "
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/manager/ModuleService.kt",
    "chars": 521,
    "preview": "package org.lsposed.lspatch.manager\n\nimport android.app.Service\nimport android.content.Intent\nimport android.os.IBinder\n"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/activity/MainActivity.kt",
    "chars": 3705,
    "preview": "package org.lsposed.lspatch.ui.activity\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport andr"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/AnywhereDropdown.kt",
    "chars": 2275,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport androidx.compose.foundation.ExperimentalFoundationApi\nimport androidx.c"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/AppItem.kt",
    "chars": 2991,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport android.graphics.drawable.GradientDrawable\nimport androidx.compose.foun"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/CenterTopBar.kt",
    "chars": 1038,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport androidx.compose.material3.CenterAlignedTopAppBar\nimport androidx.compo"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/LoadingDialog.kt",
    "chars": 1101,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport androidx.compose.foundation.background\nimport androidx.compose.foundati"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/SearchBar.kt",
    "chars": 4737,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport android.util.Log\nimport androidx.compose.animation.AnimatedVisibility\ni"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/SelectionColumn.kt",
    "chars": 3145,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport androidx.compose.animation.*\nimport androidx.compose.foundation.backgro"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/Shimmer.kt",
    "chars": 2244,
    "preview": "package org.lsposed.lspatch.ui.component\n\nimport androidx.compose.animation.core.*\nimport androidx.compose.foundation.ba"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/settings/CheckBox.kt",
    "chars": 1645,
    "preview": "package org.lsposed.lspatch.ui.component.settings\n\nimport androidx.compose.foundation.clickable\nimport androidx.compose."
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/settings/Slot.kt",
    "chars": 2294,
    "preview": "package org.lsposed.lspatch.ui.component.settings\n\nimport androidx.compose.foundation.layout.*\nimport androidx.compose.m"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/component/settings/Switch.kt",
    "chars": 1536,
    "preview": "package org.lsposed.lspatch.ui.component.settings\n\nimport androidx.compose.foundation.clickable\nimport androidx.compose."
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/BottomBarDestination.kt",
    "chars": 1133,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport androidx.annotation.StringRes\nimport androidx.compose.material.icons.Icons\ni"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/HomeScreen.kt",
    "chars": 9801,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport android.app.Activity\nimport android.content.ClipData\nimport android.content."
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/LogsScreen.kt",
    "chars": 1008,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.found"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/ManageScreen.kt",
    "chars": 3100,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.la"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/NewPatchScreen.kt",
    "chars": 24688,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport andr"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/RepoScreen.kt",
    "chars": 1008,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.found"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/SelectAppsScreen.kt",
    "chars": 5789,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport android.content.pm.ApplicationInfo\nimport android.os.Parcelable\nimport andro"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/SettingsScreen.kt",
    "chars": 10269,
    "preview": "package org.lsposed.lspatch.ui.page\n\nimport androidx.activity.compose.rememberLauncherForActivityResult\nimport androidx."
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/manage/AppManagePage.kt",
    "chars": 18587,
    "preview": "package org.lsposed.lspatch.ui.page.manage\n\nimport android.app.Activity\nimport android.content.ClipData\nimport android.c"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/page/manage/ModuleManagePage.kt",
    "chars": 4914,
    "preview": "package org.lsposed.lspatch.ui.page.manage\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.provider"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/theme/Theme.kt",
    "chars": 1364,
    "preview": "package org.lsposed.lspatch.ui.theme\n\nimport android.app.Activity\nimport android.os.Build\nimport androidx.compose.founda"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/theme/Type.kt",
    "chars": 487,
    "preview": "package org.lsposed.lspatch.ui.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.ui.text.TextS"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/util/CompositionProvider.kt",
    "chars": 273,
    "preview": "package org.lsposed.lspatch.ui.util\n\nimport androidx.compose.material3.SnackbarHostState\nimport androidx.compose.runtime"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/util/HtmlText.kt",
    "chars": 637,
    "preview": "package org.lsposed.lspatch.ui.util\n\nimport android.text.method.LinkMovementMethod\nimport android.widget.TextView\nimport"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/util/MultiDelegateState.kt",
    "chars": 979,
    "preview": "package org.lsposed.lspatch.ui.util\n\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableSta"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/util/Preview.kt",
    "chars": 245,
    "preview": "package org.lsposed.lspatch.ui.util\n\nimport androidx.compose.ui.tooling.preview.PreviewParameterProvider\n\nclass SampleSt"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/util/Utils.kt",
    "chars": 382,
    "preview": "package org.lsposed.lspatch.ui.util\n\nimport androidx.compose.foundation.lazy.LazyListState\n\nval LazyListState.lastVisibl"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/NewPatchViewModel.kt",
    "chars": 3569,
    "preview": "package org.lsposed.lspatch.ui.viewmodel\n\nimport android.util.Log\nimport androidx.compose.runtime.getValue\nimport androi"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/SelectAppsViewModel.kt",
    "chars": 1353,
    "preview": "package org.lsposed.lspatch.ui.viewmodel\n\nimport android.util.Log\nimport androidx.compose.runtime.getValue\nimport androi"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/manage/AppManageViewModel.kt",
    "chars": 5746,
    "preview": "package org.lsposed.lspatch.ui.viewmodel.manage\n\nimport android.content.pm.PackageInstaller\nimport android.util.Base64\ni"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/viewmodel/manage/ModuleManageViewModel.kt",
    "chars": 1066,
    "preview": "package org.lsposed.lspatch.ui.viewmodel.manage\n\nimport android.util.Log\nimport androidx.compose.runtime.derivedStateOf\n"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/ui/viewstate/ProcessingState.kt",
    "chars": 239,
    "preview": "package org.lsposed.lspatch.ui.viewstate\n\nsealed class ProcessingState<out T> {\n    object Idle : ProcessingState<Nothin"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/util/IntentSenderHelper.kt",
    "chars": 1162,
    "preview": "package org.lsposed.lspatch.util\n\nimport android.content.IIntentReceiver\nimport android.content.IIntentSender\nimport and"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/util/LSPPackageManager.kt",
    "chars": 10257,
    "preview": "package org.lsposed.lspatch.util\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android.co"
  },
  {
    "path": "manager/src/main/java/org/lsposed/lspatch/util/ShizukuApi.kt",
    "chars": 3173,
    "preview": "package org.lsposed.lspatch.util\n\nimport android.content.IntentSender\nimport android.content.pm.*\nimport android.os.Buil"
  },
  {
    "path": "manager/src/main/res/drawable/ic_launcher_background.xml",
    "chars": 4637,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed is free software: you can r"
  },
  {
    "path": "manager/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 2275,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed is free software: you can r"
  },
  {
    "path": "manager/src/main/res/drawable-zh-rCN/ic_launcher_background.xml",
    "chars": 3892,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed is free software: you can r"
  },
  {
    "path": "manager/src/main/res/drawable-zh-rTW/ic_launcher_background.xml",
    "chars": 3892,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ This file is part of LSPosed.\n  ~\n  ~ LSPosed is free software: you can r"
  },
  {
    "path": "manager/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 273,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "manager/src/main/res/values/strings.xml",
    "chars": 5991,
    "preview": "<resources>\n    <string name=\"add\">Add</string>\n    <string name=\"install\">Install</string>\n    <string name=\"installing"
  },
  {
    "path": "manager/src/main/res/values/strings_untranslatable.xml",
    "chars": 130,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\" translatable=\"false\">LSPatch</string>\n</r"
  },
  {
    "path": "manager/src/main/res/values-af/strings.xml",
    "chars": 6392,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Voeg by</string>\n    <string name=\"install\">In"
  },
  {
    "path": "manager/src/main/res/values-ar/strings.xml",
    "chars": 5944,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">إضافة</string>\n    <string name=\"install\">تثبي"
  },
  {
    "path": "manager/src/main/res/values-bg/strings.xml",
    "chars": 6643,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Добавяне на</string>\n    <string name=\"install"
  },
  {
    "path": "manager/src/main/res/values-bn/strings.xml",
    "chars": 5992,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">যোগ করুন</string>\n    <string name=\"install\">ই"
  },
  {
    "path": "manager/src/main/res/values-ca/strings.xml",
    "chars": 6632,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Afegeix</string>\n    <string name=\"install\">In"
  },
  {
    "path": "manager/src/main/res/values-cs/strings.xml",
    "chars": 6233,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Přidat</string>\n    <string name=\"install\">Ins"
  },
  {
    "path": "manager/src/main/res/values-da/strings.xml",
    "chars": 6256,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Tilføje</string>\n    <string name=\"install\">In"
  },
  {
    "path": "manager/src/main/res/values-de/strings.xml",
    "chars": 6623,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Hinzufügen</string>\n    <string name=\"install\""
  },
  {
    "path": "manager/src/main/res/values-el/strings.xml",
    "chars": 6773,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Προσθήκη</string>\n    <string name=\"install\">Ε"
  },
  {
    "path": "manager/src/main/res/values-es/strings.xml",
    "chars": 6593,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Agregar</string>\n    <string name=\"install\">In"
  },
  {
    "path": "manager/src/main/res/values-et/strings.xml",
    "chars": 6296,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Lisama</string>\n    <string name=\"install\">Ins"
  },
  {
    "path": "manager/src/main/res/values-fa/strings.xml",
    "chars": 6153,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">افزودن</string>\n    <string name=\"install\">نصب"
  },
  {
    "path": "manager/src/main/res/values-fi/strings.xml",
    "chars": 6389,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Lisätä</string>\n    <string name=\"install\">Ase"
  },
  {
    "path": "manager/src/main/res/values-fr/strings.xml",
    "chars": 6721,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Ajouter</string>\n    <string name=\"install\">In"
  },
  {
    "path": "manager/src/main/res/values-hi/strings.xml",
    "chars": 6161,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">जोड़ना</string>\n    <string name=\"install\">इंस"
  },
  {
    "path": "manager/src/main/res/values-hr/strings.xml",
    "chars": 6402,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Dodaj</string>\n    <string name=\"install\">Inst"
  },
  {
    "path": "manager/src/main/res/values-hu/strings.xml",
    "chars": 6436,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Hozzáadás</string>\n    <string name=\"install\">"
  },
  {
    "path": "manager/src/main/res/values-in/strings.xml",
    "chars": 6259,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Tambah</string>\n    <string name=\"install\">Ins"
  },
  {
    "path": "manager/src/main/res/values-it/strings.xml",
    "chars": 6543,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Aggiungi</string>\n    <string name=\"install\">I"
  },
  {
    "path": "manager/src/main/res/values-iw/strings.xml",
    "chars": 5845,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">לְהוֹסִיף</string>\n    <string name=\"install\">"
  },
  {
    "path": "manager/src/main/res/values-ja/strings.xml",
    "chars": 5362,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">追加</string>\n    <string name=\"install\">インストール<"
  },
  {
    "path": "manager/src/main/res/values-ko/strings.xml",
    "chars": 5162,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">추가</string>\n    <string name=\"install\">설치</str"
  },
  {
    "path": "manager/src/main/res/values-ku/strings.xml",
    "chars": 6209,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Lêzêdekirin</string>\n    <string name=\"install"
  },
  {
    "path": "manager/src/main/res/values-lt/strings.xml",
    "chars": 6432,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Papildyti</string>\n    <string name=\"install\">"
  },
  {
    "path": "manager/src/main/res/values-nl/strings.xml",
    "chars": 6449,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Toevoegen</string>\n    <string name=\"install\">"
  },
  {
    "path": "manager/src/main/res/values-no/strings.xml",
    "chars": 6170,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Legge til</string>\n    <string name=\"install\">"
  },
  {
    "path": "manager/src/main/res/values-pl/strings.xml",
    "chars": 6462,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"add\">Dodaj</string>\n    <string name=\"install\">Zain"
  }
]

// ... and 60 more files (download for full content)

About this extraction

This page contains the full source code of the LSPosed/LSPatch GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 260 files (1.1 MB), approximately 275.5k tokens, and a symbol index with 841 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!