first commit
This commit is contained in:
196
pkg/android_binary/apk/apk.go
Normal file
196
pkg/android_binary/apk/apk.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package apk
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/android_binary"
|
||||
"image"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
_ "image/jpeg" // handle jpeg format
|
||||
_ "image/png" // handle png format
|
||||
)
|
||||
|
||||
// Apk is an application package file for android.
|
||||
type Apk struct {
|
||||
f *os.File
|
||||
zipreader *zip.Reader
|
||||
manifest Manifest
|
||||
table *android_binary.TableFile
|
||||
}
|
||||
|
||||
// OpenFile will open the file specified by filename and return Apk
|
||||
func OpenFile(filename string) (apk *Apk, err error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apk, err = OpenZipReader(f, fi.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apk.f = f
|
||||
return
|
||||
}
|
||||
|
||||
// OpenZipReader has same arguments like zip.NewReader
|
||||
func OpenZipReader(r io.ReaderAt, size int64) (*Apk, error) {
|
||||
zipreader, err := zip.NewReader(r, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apk := &Apk{
|
||||
zipreader: zipreader,
|
||||
}
|
||||
if err = apk.parseResources(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = apk.parseManifest(); err != nil {
|
||||
return nil, errorf("parse-manifest: %w", err)
|
||||
}
|
||||
return apk, nil
|
||||
}
|
||||
|
||||
// Close is avaliable only if apk is created with OpenFile
|
||||
func (k *Apk) Close() error {
|
||||
if k.f == nil {
|
||||
return nil
|
||||
}
|
||||
return k.f.Close()
|
||||
}
|
||||
|
||||
// Icon returns the icon image of the APK.
|
||||
func (k *Apk) Icon(resConfig *android_binary.ResTableConfig) (image.Image, error) {
|
||||
iconPath, err := k.manifest.App.Icon.WithResTableConfig(resConfig).String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if android_binary.IsResID(iconPath) {
|
||||
return nil, newError("unable to convert icon-id to icon path")
|
||||
}
|
||||
imgData, err := k.readZipFile(iconPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m, _, err := image.Decode(bytes.NewReader(imgData))
|
||||
return m, err
|
||||
}
|
||||
|
||||
// Label returns the label of the APK.
|
||||
func (k *Apk) Label(resConfig *android_binary.ResTableConfig) (s string, err error) {
|
||||
s, err = k.manifest.App.Label.WithResTableConfig(resConfig).String()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if android_binary.IsResID(s) {
|
||||
err = newError("unable to convert label-id to string")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Manifest returns the manifest of the APK.
|
||||
func (k *Apk) Manifest() Manifest {
|
||||
return k.manifest
|
||||
}
|
||||
|
||||
// PackageName returns the package name of the APK.
|
||||
func (k *Apk) PackageName() string {
|
||||
return k.manifest.Package.MustString()
|
||||
}
|
||||
|
||||
func isMainIntentFilter(intent ActivityIntentFilter) bool {
|
||||
ok := false
|
||||
for _, action := range intent.Actions {
|
||||
s, err := action.Name.String()
|
||||
if err == nil && s == "android.intent.action.MAIN" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
ok = false
|
||||
for _, category := range intent.Categories {
|
||||
s, err := category.Name.String()
|
||||
if err == nil && s == "android.intent.category.LAUNCHER" {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// MainActivity returns the name of the main activity.
|
||||
func (k *Apk) MainActivity() (activity string, err error) {
|
||||
for _, act := range k.manifest.App.Activities {
|
||||
for _, intent := range act.IntentFilters {
|
||||
if isMainIntentFilter(intent) {
|
||||
return act.Name.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, act := range k.manifest.App.ActivityAliases {
|
||||
for _, intent := range act.IntentFilters {
|
||||
if isMainIntentFilter(intent) {
|
||||
return act.TargetActivity.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", newError("No main activity found")
|
||||
}
|
||||
|
||||
func (k *Apk) parseManifest() error {
|
||||
xmlData, err := k.readZipFile("AndroidManifest.xml")
|
||||
if err != nil {
|
||||
return errorf("failed to read AndroidManifest.xml: %w", err)
|
||||
}
|
||||
xmlfile, err := android_binary.NewXMLFile(bytes.NewReader(xmlData))
|
||||
if err != nil {
|
||||
return errorf("failed to parse AndroidManifest.xml: %w", err)
|
||||
}
|
||||
return xmlfile.Decode(&k.manifest, k.table, nil)
|
||||
}
|
||||
|
||||
func (k *Apk) parseResources() (err error) {
|
||||
resData, err := k.readZipFile("resources.arsc")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
k.table, err = android_binary.NewTableFile(bytes.NewReader(resData))
|
||||
return
|
||||
}
|
||||
|
||||
func (k *Apk) readZipFile(name string) (data []byte, err error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
for _, file := range k.zipreader.File {
|
||||
if file.Name != name {
|
||||
continue
|
||||
}
|
||||
rc, er := file.Open()
|
||||
if er != nil {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
_, err = io.Copy(buf, rc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("File %s not found", strconv.Quote(name))
|
||||
}
|
108
pkg/android_binary/apk/apkxml.go
Normal file
108
pkg/android_binary/apk/apkxml.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package apk
|
||||
|
||||
import "git.bvbej.com/bvbej/base-golang/pkg/android_binary"
|
||||
|
||||
// Instrumentation is an application instrumentation code.
|
||||
type Instrumentation struct {
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
Target android_binary.String `xml:"http://schemas.android.com/apk/res/android targetPackage,attr"`
|
||||
HandleProfiling android_binary.Bool `xml:"http://schemas.android.com/apk/res/android handleProfiling,attr"`
|
||||
FunctionalTest android_binary.Bool `xml:"http://schemas.android.com/apk/res/android functionalTest,attr"`
|
||||
}
|
||||
|
||||
// ActivityAction is an action of an activity.
|
||||
type ActivityAction struct {
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
}
|
||||
|
||||
// ActivityCategory is a category of an activity.
|
||||
type ActivityCategory struct {
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
}
|
||||
|
||||
// ActivityIntentFilter is an androidbinary.Int32ent filter of an activity.
|
||||
type ActivityIntentFilter struct {
|
||||
Actions []ActivityAction `xml:"action"`
|
||||
Categories []ActivityCategory `xml:"category"`
|
||||
}
|
||||
|
||||
// AppActivity is an activity in an application.
|
||||
type AppActivity struct {
|
||||
Theme android_binary.String `xml:"http://schemas.android.com/apk/res/android theme,attr"`
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
Label android_binary.String `xml:"http://schemas.android.com/apk/res/android label,attr"`
|
||||
ScreenOrientation android_binary.String `xml:"http://schemas.android.com/apk/res/android screenOrientation,attr"`
|
||||
IntentFilters []ActivityIntentFilter `xml:"intent-filter"`
|
||||
}
|
||||
|
||||
// AppActivityAlias https://developer.android.com/guide/topics/manifest/activity-alias-element
|
||||
type AppActivityAlias struct {
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
Label android_binary.String `xml:"http://schemas.android.com/apk/res/android label,attr"`
|
||||
TargetActivity android_binary.String `xml:"http://schemas.android.com/apk/res/android targetActivity,attr"`
|
||||
IntentFilters []ActivityIntentFilter `xml:"intent-filter"`
|
||||
}
|
||||
|
||||
// MetaData is a metadata in an application.
|
||||
type MetaData struct {
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
Value android_binary.String `xml:"http://schemas.android.com/apk/res/android value,attr"`
|
||||
}
|
||||
|
||||
// Application is an application in an APK.
|
||||
type Application struct {
|
||||
AllowTaskReparenting android_binary.Bool `xml:"http://schemas.android.com/apk/res/android allowTaskReparenting,attr"`
|
||||
AllowBackup android_binary.Bool `xml:"http://schemas.android.com/apk/res/android allowBackup,attr"`
|
||||
BackupAgent android_binary.String `xml:"http://schemas.android.com/apk/res/android backupAgent,attr"`
|
||||
Debuggable android_binary.Bool `xml:"http://schemas.android.com/apk/res/android debuggable,attr"`
|
||||
Description android_binary.String `xml:"http://schemas.android.com/apk/res/android description,attr"`
|
||||
Enabled android_binary.Bool `xml:"http://schemas.android.com/apk/res/android enabled,attr"`
|
||||
HasCode android_binary.Bool `xml:"http://schemas.android.com/apk/res/android hasCode,attr"`
|
||||
HardwareAccelerated android_binary.Bool `xml:"http://schemas.android.com/apk/res/android hardwareAccelerated,attr"`
|
||||
Icon android_binary.String `xml:"http://schemas.android.com/apk/res/android icon,attr"`
|
||||
KillAfterRestore android_binary.Bool `xml:"http://schemas.android.com/apk/res/android killAfterRestore,attr"`
|
||||
LargeHeap android_binary.Bool `xml:"http://schemas.android.com/apk/res/android largeHeap,attr"`
|
||||
Label android_binary.String `xml:"http://schemas.android.com/apk/res/android label,attr"`
|
||||
Logo android_binary.String `xml:"http://schemas.android.com/apk/res/android logo,attr"`
|
||||
ManageSpaceActivity android_binary.String `xml:"http://schemas.android.com/apk/res/android manageSpaceActivity,attr"`
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
Permission android_binary.String `xml:"http://schemas.android.com/apk/res/android permission,attr"`
|
||||
Persistent android_binary.Bool `xml:"http://schemas.android.com/apk/res/android persistent,attr"`
|
||||
Process android_binary.String `xml:"http://schemas.android.com/apk/res/android process,attr"`
|
||||
RestoreAnyVersion android_binary.Bool `xml:"http://schemas.android.com/apk/res/android restoreAnyVersion,attr"`
|
||||
RequiredAccountType android_binary.String `xml:"http://schemas.android.com/apk/res/android requiredAccountType,attr"`
|
||||
RestrictedAccountType android_binary.String `xml:"http://schemas.android.com/apk/res/android restrictedAccountType,attr"`
|
||||
SupportsRtl android_binary.Bool `xml:"http://schemas.android.com/apk/res/android supportsRtl,attr"`
|
||||
TaskAffinity android_binary.String `xml:"http://schemas.android.com/apk/res/android taskAffinity,attr"`
|
||||
TestOnly android_binary.Bool `xml:"http://schemas.android.com/apk/res/android testOnly,attr"`
|
||||
Theme android_binary.String `xml:"http://schemas.android.com/apk/res/android theme,attr"`
|
||||
UIOptions android_binary.String `xml:"http://schemas.android.com/apk/res/android uiOptions,attr"`
|
||||
VMSafeMode android_binary.Bool `xml:"http://schemas.android.com/apk/res/android vmSafeMode,attr"`
|
||||
Activities []AppActivity `xml:"activity"`
|
||||
ActivityAliases []AppActivityAlias `xml:"activity-alias"`
|
||||
MetaData []MetaData `xml:"meta-data"`
|
||||
}
|
||||
|
||||
// UsesSDK is target SDK version.
|
||||
type UsesSDK struct {
|
||||
Min android_binary.Int32 `xml:"http://schemas.android.com/apk/res/android minSdkVersion,attr"`
|
||||
Target android_binary.Int32 `xml:"http://schemas.android.com/apk/res/android targetSdkVersion,attr"`
|
||||
Max android_binary.Int32 `xml:"http://schemas.android.com/apk/res/android maxSdkVersion,attr"`
|
||||
}
|
||||
|
||||
// UsesPermission is user grant the system permission.
|
||||
type UsesPermission struct {
|
||||
Name android_binary.String `xml:"http://schemas.android.com/apk/res/android name,attr"`
|
||||
Max android_binary.Int32 `xml:"http://schemas.android.com/apk/res/android maxSdkVersion,attr"`
|
||||
}
|
||||
|
||||
// Manifest is a manifest of an APK.
|
||||
type Manifest struct {
|
||||
Package android_binary.String `xml:"package,attr"`
|
||||
VersionCode android_binary.Int32 `xml:"http://schemas.android.com/apk/res/android versionCode,attr"`
|
||||
VersionName android_binary.String `xml:"http://schemas.android.com/apk/res/android versionName,attr"`
|
||||
App Application `xml:"application"`
|
||||
Instrument Instrumentation `xml:"instrumentation"`
|
||||
SDK UsesSDK `xml:"uses-sdk"`
|
||||
UsesPermissions []UsesPermission `xml:"uses-permission"`
|
||||
}
|
9
pkg/android_binary/apk/errors.go
Normal file
9
pkg/android_binary/apk/errors.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package apk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var newError = errors.New
|
||||
var errorf = fmt.Errorf
|
Reference in New Issue
Block a user