@devicefarmer/adbkit-apkreader
v4.0.0
Published
Extracts information from APK files.
Downloads
228,342
Readme
adbkit-apkreader
adbkit-apkreader provides a Node.js API for extracting information from Android APK files. For example, it allows you to read the AndroidManifest.xml of an existing APK file.
Requirements
- Node.js 22 or newer. Older versions are not supported.
Getting started
Install via NPM:
npm install --save @devicefarmer/adbkit-apkreaderExamples
Read the AndroidManifest.xml of an APK
const util = require('util')
const ApkReader = require('@devicefarmer/adbkit-apkreader')
ApkReader.open('HelloApp.apk')
.then(reader => reader.readManifest())
.then(manifest => console.log(util.inspect(manifest, { depth: null })))TypeScript
The package ships its own type declarations. The shapes of the parsed results are exported on the ApkReader namespace:
import ApkReader from '@devicefarmer/adbkit-apkreader'
const reader = await ApkReader.open('HelloApp.apk')
const manifest: ApkReader.Manifest = await reader.readManifest()
// application has no lists when the manifest has no <application> element.
console.log(manifest.package, manifest.application.launcherActivities ?? [])With esModuleInterop off, use import ApkReader = require('@devicefarmer/adbkit-apkreader') instead. Other exported types include ApkReader.XmlElement, ApkReader.TypedValue and ApkReader.Attributes.
API
Every method that returns a Promise returns a native one. Versions 3.x and earlier returned Bluebird promises; if you relied on Bluebird-only methods such as .timeout(), .tap() or .return(), wrap the result with Bluebird.resolve() or use the native equivalents.
ApkReader
ApkReader.MANIFEST
A convenience constant with the value 'AndroidManifest.xml'. Can use useful with other API methods in certain circumstances.
ApkReader.open(file)
Alternate syntax to manually creating an ApkReader instance. Files and Buffers are supported, but support for streams might be added at some point.
Note that currently this method cannot reject as the file is opened lazily, but this may change in the future and therefore returns a Promise for fewer future compatibility issues. On a related node, calling the constructor directly is still possible, but discouraged.
- file The path to the APK file, or a
Bufferwith its contents. - Returns: A
Promisethat resolves with anApkReaderinstance.
reader.readContent(path)
Reads the content of the given file inside the APK.
- path The path to the file. For example, giving
'META-INF/MANIFEST.MF'as the path would read the content of that file. - Returns: A
Promisethat resolves with aBuffercontaining the full contents of the file.
reader.readManifest([options])
Reads and parses the AndroidManifest.xml file inside the APK and returns a simplified object representation of it.
- options Optional. An
Objectwith the following properties:- debug If
true, logs every read of the binary XML parser to the console. Defaults tofalse.
- debug If
- Returns: A
Promisethat resolves with a JavaScriptObjectrepresentation of the manifest. See example output below. Rejects on error (e.g. if parsing was unsuccessful).
{ versionCode: 1,
versionName: '1.0',
package: 'com.example.hello.helloapp.app',
usesPermissions: [],
permissions: [],
permissionTrees: [],
permissionGroups: [],
instrumentation: null,
usesSdk: { minSdkVersion: 7, targetSdkVersion: 19 },
usesConfiguration: null,
usesFeatures: [],
supportsScreens: null,
compatibleScreens: [],
supportsGlTextures: [],
application:
{ theme: 'resourceId:0x7f0b0000',
label: 'resourceId:0x7f0a000e',
icon: 'resourceId:0x7f020057',
debuggable: true,
allowBackup: true,
activities:
[ { label: 'resourceId:0x7f0a000e',
name: 'com.example.hello.helloapp.app.MainActivity',
intentFilters:
[ { actions: [ { name: 'android.intent.action.MAIN' } ],
categories: [ { name: 'android.intent.category.LAUNCHER' } ],
data: [] } ],
metaData: [] } ],
activityAliases: [],
launcherActivities:
[ { label: 'resourceId:0x7f0a000e',
name: 'com.example.hello.helloapp.app.MainActivity',
intentFilters:
[ { actions: [ { name: 'android.intent.action.MAIN' } ],
categories: [ { name: 'android.intent.category.LAUNCHER' } ],
data: [] } ],
metaData: [] } ],
services: [],
receivers: [],
providers: [],
usesLibraries: [] } }reader.readXml(path[, options])
Reads and parses the binary XML file at the given path inside the APK file. Attempts to be somewhat compatible with the DOM API.
- path The path to the binary XML file inside the APK. For example, giving
'AndroidManifest.xml'as the path would parse the manifest (but you'll probably want to usereader.readManifest()instead). - options Optional. The same options as
reader.readManifest(). - Returns: A
Promisethat resolves with a JavaScriptObjectrepresentation of the root node of the XML file. All nodes including the root node have the properties listed below. Rejects on error (e.g. if parsing was unsuccessful).- namespaceURI The namespace URI or
nullif none. - nodeType
1for element nodes,2for attribute nodes, and4for CData sections. - nodeName The node name.
- For element nodes, the following additional properties are present:
- attributes An array of attribute nodes.
- childNodes An array of child nodes.
- For attribute nodes, the following additional properties are present:
- name The attribute name.
- value The attribute value, if possible to represent as a simple value.
- typedValue May be available when the attribute represents a complex value. See android.util.TypedValue for more information. Has the following properties:
- value The value, which might
null,String,Boolean,Numberor even anObjectfor the most complex types. - type A
Stringrepresentation of the type of the value. - rawType A raw integer presentation of the type of the value.
- value The value, which might
- For CData nodes, the following additional properties are present:
- data The CData.
- typedValue May be available if the section represents a more complex type. See above for details.
- namespaceURI The namespace URI or
reader.usingFileStream(path, action)
Opens a readable Stream to the given file inside the APK and runs the given action with it. The APK file is kept open while the action runs, allowing you to process the stream. Once the action finishes, the APK will be automatically closed.
- path The path to the file. For example, giving
'META-INF/MANIFEST.MF'as the path would open that file. - action(stream) A function that processes the stream somehow. MUST return a
Promisethat resolves when you're done processing the stream. The value that thePromiseresolves with will also be the value thatusingFileStream()resolves with.- stream A readable Stream of the file content. You should either consume or end the stream yourself before resolving the action.
- Returns: A
Promisethat resolves with whateveractionresolves with.
More information
- android.util.TypedValue For more information about value types.
- Dong Liu's excellent Java-based APK parser, which was used as a reference implementation.
- A detailed blog port about Android's binary XML format
- Stackoverflow discussion about the topic
- android-apktool The most advanced CLI/Java-based APK tool.
Releasing
The package is published to npm as @devicefarmer/adbkit-apkreader by two GitHub Actions workflows:
- Run
Prepare releasewith the version as input, for example3.2.5. It bumpspackage.json, writes theCHANGELOG.mdsection from GitHub's generated notes, and pushesrelease/v3.2.5. It writes nothing tomaster. - Open the pull request it links to in the job summary, label it
ignore-for-release, and merge it once the checks pass. - Run
Releaseonmasterwith the same version. It tagsv3.2.5, publishes the GitHub release with the notes fromCHANGELOG.md, then compiles the TypeScript, tests and publishes the package to npm with trusted publishing (OIDC), so no npm token is stored anywhere.
The bump goes through a pull request because master is protected. Release refuses to run on any branch but master, so an unmerged release/v* branch cannot be published.
Contributing
See CONTRIBUTING.md.
License
See LICENSE.
Copyright © The OpenSTF Project. All Rights Reserved.
