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