apk.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package apk
  2. import (
  3. "archive/zip"
  4. "bytes"
  5. "fmt"
  6. "git.bvbej.com/bvbej/base-golang/pkg/android_binary"
  7. "image"
  8. "io"
  9. "os"
  10. "strconv"
  11. _ "image/jpeg" // handle jpeg format
  12. _ "image/png" // handle png format
  13. )
  14. // Apk is an application package file for android.
  15. type Apk struct {
  16. f *os.File
  17. zipreader *zip.Reader
  18. manifest Manifest
  19. table *android_binary.TableFile
  20. }
  21. // OpenFile will open the file specified by filename and return Apk
  22. func OpenFile(filename string) (apk *Apk, err error) {
  23. f, err := os.Open(filename)
  24. if err != nil {
  25. return nil, err
  26. }
  27. defer func() {
  28. if err != nil {
  29. f.Close()
  30. }
  31. }()
  32. fi, err := f.Stat()
  33. if err != nil {
  34. return nil, err
  35. }
  36. apk, err = OpenZipReader(f, fi.Size())
  37. if err != nil {
  38. return nil, err
  39. }
  40. apk.f = f
  41. return
  42. }
  43. // OpenZipReader has same arguments like zip.NewReader
  44. func OpenZipReader(r io.ReaderAt, size int64) (*Apk, error) {
  45. zipreader, err := zip.NewReader(r, size)
  46. if err != nil {
  47. return nil, err
  48. }
  49. apk := &Apk{
  50. zipreader: zipreader,
  51. }
  52. if err = apk.parseResources(); err != nil {
  53. return nil, err
  54. }
  55. if err = apk.parseManifest(); err != nil {
  56. return nil, errorf("parse-manifest: %w", err)
  57. }
  58. return apk, nil
  59. }
  60. // Close is avaliable only if apk is created with OpenFile
  61. func (k *Apk) Close() error {
  62. if k.f == nil {
  63. return nil
  64. }
  65. return k.f.Close()
  66. }
  67. // Icon returns the icon image of the APK.
  68. func (k *Apk) Icon(resConfig *android_binary.ResTableConfig) (image.Image, error) {
  69. iconPath, err := k.manifest.App.Icon.WithResTableConfig(resConfig).String()
  70. if err != nil {
  71. return nil, err
  72. }
  73. if android_binary.IsResID(iconPath) {
  74. return nil, newError("unable to convert icon-id to icon path")
  75. }
  76. imgData, err := k.readZipFile(iconPath)
  77. if err != nil {
  78. return nil, err
  79. }
  80. m, _, err := image.Decode(bytes.NewReader(imgData))
  81. return m, err
  82. }
  83. // Label returns the label of the APK.
  84. func (k *Apk) Label(resConfig *android_binary.ResTableConfig) (s string, err error) {
  85. s, err = k.manifest.App.Label.WithResTableConfig(resConfig).String()
  86. if err != nil {
  87. return
  88. }
  89. if android_binary.IsResID(s) {
  90. err = newError("unable to convert label-id to string")
  91. }
  92. return
  93. }
  94. // Manifest returns the manifest of the APK.
  95. func (k *Apk) Manifest() Manifest {
  96. return k.manifest
  97. }
  98. // PackageName returns the package name of the APK.
  99. func (k *Apk) PackageName() string {
  100. return k.manifest.Package.MustString()
  101. }
  102. func isMainIntentFilter(intent ActivityIntentFilter) bool {
  103. ok := false
  104. for _, action := range intent.Actions {
  105. s, err := action.Name.String()
  106. if err == nil && s == "android.intent.action.MAIN" {
  107. ok = true
  108. break
  109. }
  110. }
  111. if !ok {
  112. return false
  113. }
  114. ok = false
  115. for _, category := range intent.Categories {
  116. s, err := category.Name.String()
  117. if err == nil && s == "android.intent.category.LAUNCHER" {
  118. ok = true
  119. break
  120. }
  121. }
  122. return ok
  123. }
  124. // MainActivity returns the name of the main activity.
  125. func (k *Apk) MainActivity() (activity string, err error) {
  126. for _, act := range k.manifest.App.Activities {
  127. for _, intent := range act.IntentFilters {
  128. if isMainIntentFilter(intent) {
  129. return act.Name.String()
  130. }
  131. }
  132. }
  133. for _, act := range k.manifest.App.ActivityAliases {
  134. for _, intent := range act.IntentFilters {
  135. if isMainIntentFilter(intent) {
  136. return act.TargetActivity.String()
  137. }
  138. }
  139. }
  140. return "", newError("No main activity found")
  141. }
  142. func (k *Apk) parseManifest() error {
  143. xmlData, err := k.readZipFile("AndroidManifest.xml")
  144. if err != nil {
  145. return errorf("failed to read AndroidManifest.xml: %w", err)
  146. }
  147. xmlfile, err := android_binary.NewXMLFile(bytes.NewReader(xmlData))
  148. if err != nil {
  149. return errorf("failed to parse AndroidManifest.xml: %w", err)
  150. }
  151. return xmlfile.Decode(&k.manifest, k.table, nil)
  152. }
  153. func (k *Apk) parseResources() (err error) {
  154. resData, err := k.readZipFile("resources.arsc")
  155. if err != nil {
  156. return
  157. }
  158. k.table, err = android_binary.NewTableFile(bytes.NewReader(resData))
  159. return
  160. }
  161. func (k *Apk) readZipFile(name string) (data []byte, err error) {
  162. buf := bytes.NewBuffer(nil)
  163. for _, file := range k.zipreader.File {
  164. if file.Name != name {
  165. continue
  166. }
  167. rc, er := file.Open()
  168. if er != nil {
  169. err = er
  170. return
  171. }
  172. defer rc.Close()
  173. _, err = io.Copy(buf, rc)
  174. if err != nil {
  175. return
  176. }
  177. return buf.Bytes(), nil
  178. }
  179. return nil, fmt.Errorf("File %s not found", strconv.Quote(name))
  180. }