apkmanager.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package main
  2. import (
  3. "regexp"
  4. "strconv"
  5. "strings"
  6. "time"
  7. "github.com/pkg/errors"
  8. "github.com/shogo82148/androidbinary/apk"
  9. )
  10. var canFixedInstallFails = map[string]bool{
  11. "INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE": true,
  12. "INSTALL_FAILED_UPDATE_INCOMPATIBLE": true,
  13. "INSTALL_FAILED_VERSION_DOWNGRADE": true,
  14. }
  15. type APKManager struct {
  16. Path string
  17. packageName string
  18. mainActivity string
  19. }
  20. func (am *APKManager) PackageName() (string, error) {
  21. if am.packageName != "" {
  22. return am.packageName, nil
  23. }
  24. pkg, err := apk.OpenFile(am.Path)
  25. if err != nil {
  26. return "", errors.Wrap(err, "apk parse")
  27. }
  28. defer pkg.Close()
  29. am.packageName = pkg.PackageName()
  30. am.mainActivity, _ = pkg.MainActivity()
  31. return am.packageName, nil
  32. }
  33. func (am *APKManager) Install() error {
  34. sdk, _ := strconv.Atoi(getCachedProperty("ro.build.version.sdk"))
  35. cmds := []string{"pm", "install", "-d", "-r", am.Path}
  36. if sdk >= 23 { // android 6.0
  37. cmds = []string{"pm", "install", "-d", "-r", "-g", am.Path}
  38. }
  39. out, err := runShell(cmds...)
  40. if err != nil {
  41. matches := regexp.MustCompile(`Failure \[([\w_ ]+)\]`).FindStringSubmatch(string(out))
  42. if len(matches) > 0 {
  43. return errors.Wrap(err, matches[0])
  44. }
  45. return errors.Wrap(err, string(out))
  46. }
  47. return nil
  48. }
  49. func (am *APKManager) ForceInstall() error {
  50. err := am.Install()
  51. if err == nil {
  52. return nil
  53. }
  54. errType := regexp.MustCompile(`INSTALL_FAILED_[\w_]+`).FindString(err.Error())
  55. if !canFixedInstallFails[errType] {
  56. return err
  57. }
  58. log.Infof("install meet %v, try to uninstall", errType)
  59. packageName, err := am.PackageName()
  60. if err != nil {
  61. return errors.Wrap(err, "apk parse")
  62. }
  63. log.Infof("uninstall %s", packageName)
  64. runShell("pm", "uninstall", packageName)
  65. return am.Install()
  66. }
  67. type StartOptions struct {
  68. Stop bool
  69. Wait bool
  70. }
  71. func (am *APKManager) Start(opts StartOptions) error {
  72. packageName, err := am.PackageName()
  73. if err != nil {
  74. return err
  75. }
  76. if am.mainActivity == "" {
  77. return errors.New("parse MainActivity failed")
  78. }
  79. mainActivity := am.mainActivity
  80. if !strings.Contains(mainActivity, ".") {
  81. mainActivity = "." + mainActivity
  82. }
  83. _, err = runShellTimeout(30*time.Second, "am", "start", "-n", packageName+"/"+mainActivity)
  84. return err
  85. }
  86. func installAPK(path string) error {
  87. am := &APKManager{Path: path}
  88. return am.Install()
  89. }
  90. func forceInstallAPK(filepath string) error {
  91. am := &APKManager{Path: filepath}
  92. return am.ForceInstall()
  93. }