// AppIDFromString returns the app id (or base uid) for a given uid, stripping out the user id from it.
// (ie. "10001" -> 10001,nil; "u0a25" -> 10025,nil; "text" -> 0,error
func AppIDFromString(uid string) (int32, error) {
	// The empty string is a valid/expected value to pass through here.
	if uid == "" {
		return 0, nil
	}

	if m, result := parseutils.SubexpNames(abrUIDRE, uid); m {
		i, err := strconv.Atoi(result["appId"])
		if err != nil {
			fmt.Printf("Error getting app id from string: %v\n", err)
			return 0, err
		}
		// These are types defined and formatted in frameworks/base/core/java/android/os/UserHandle.java
		switch result["aidType"] {
		case "i": // Isolated UID
			return int32(i) + firstIsolatedUID, nil
		case "a": // appId >= firstApplicationUID
			return int32(i) + firstApplicationUID, nil
		case "s": // Unmodified app ID
			return int32(i), nil
		default:
			return int32(i), fmt.Errorf("unknown appIdType: %s", result["aidType"])
		}
	}

	i, err := strconv.Atoi(uid)
	if err != nil {
		fmt.Printf("Error getting app id from string: %v\n", err)
		return 0, err
	}
	return AppID(int32(i)), nil
}
func sdkVersion(input string) (int, error) {
	// Found in the System Properties section of a bug report.
	re := regexp.MustCompile(`.*\[ro.build.version.sdk\]:\s+\[(?P<sdkVersion>\d+)\].*`)
	if match, result := parseutils.SubexpNames(re, input); match {
		return strconv.Atoi(result["sdkVersion"])
	}
	return -1, errors.New("unable to find device SDK version")
}
// modelName returns the device's model name (ie. Nexus 5).
func modelName(input string) string {
	// Found in the System Properties section of a bug report.
	re := regexp.MustCompile(`.*\[ro.product.model\]:\s+\[(?P<modelName>.*)\].*`)
	if match, result := parseutils.SubexpNames(re, input); match {
		return result["modelName"]
	}
	// We should only get to this point in the case of a bad (malformed) bug report.
	return "unknown device"
}
// extractAppsFromAppOpsDump looks at the app ops service dump from a bug report
// and extracts package names and their UIDs from the dump. It returns a mapping of
// the package name to the PackageInfo object.
func extractAppsFromAppOpsDump(s string) (map[string]*usagepb.PackageInfo, []error) {
	pkgs := make(map[string]*usagepb.PackageInfo)
	var errs []error

	inAppOpsSection := false
	var curUID int32
	var err error

Loop:
	for _, line := range strings.Split(s, "\n") {
		line = strings.TrimSpace(line)
		if m, result := parseutils.SubexpNames(serviceDumpRE, line); m {
			switch in := result["service"] == "appops"; {
			case inAppOpsSection && !in: // Just exited the App Ops section
				break Loop
			case in:
				inAppOpsSection = true
				continue Loop
			default: // Random section
				continue Loop
			}
		}
		if !inAppOpsSection {
			continue
		}
		if m, result := parseutils.SubexpNames(uidRE, line); m {
			curUID, err = AppIDFromString(result["uid"])
			if err != nil {
				errs = append(errs, err)
			}
		}
		if m, result := parseutils.SubexpNames(appOpsPackageRE, line); m {
			pkg := result["package"]
			pkgs[pkg] = &usagepb.PackageInfo{
				PkgName: proto.String(pkg),
				Uid:     proto.Int32(curUID),
			}
		}
	}

	return pkgs, errs
}
func extractBuildFingerprint(input string) string {
	// A regular expression to match any build fingerprint line in the bugreport.
	re := regexp.MustCompile("Build\\s+fingerprint:\\s+'(?P<build>\\S+)'")
	var out string
	for _, line := range strings.Split(input, "\n") {
		if match, result := parseutils.SubexpNames(re, line); match {
			out = result["build"]
			break
		}
	}
	return out
}
// extractAppsFromPackageDump looks at the package service dump from a bug report
// and extracts as much application info from the dump. It returns a mapping of
// the package name to the PackageInfo object.
func extractAppsFromPackageDump(s string) (map[string]*usagepb.PackageInfo, []error) {
	pkgs := make(map[string]*usagepb.PackageInfo)
	var errs []error

	var inPackageDumpSection, inCurrentSection bool
	var curPkg *usagepb.PackageInfo

Loop:
	for _, line := range strings.Split(s, "\n") {
		line = strings.TrimSpace(line)
		if m, result := parseutils.SubexpNames(serviceDumpRE, line); m {
			switch in := result["service"] == "package"; {
			case inPackageDumpSection && !in: // Just exited package dump section
				break Loop
			case in:
				inPackageDumpSection = true
				continue Loop
			default: // Random section
				continue Loop
			}
		}
		if !inPackageDumpSection {
			continue
		}
		switch line {
		case "Packages:":
			inCurrentSection = true
			continue Loop
		case "Hidden system packages:":
			inCurrentSection = false
			break Loop
		}
		if !inCurrentSection {
			continue
		}
		if m, result := parseutils.SubexpNames(packageDumpPackageRE, line); m {
			if curPkg != nil {
				pkgs[curPkg.GetPkgName()] = curPkg
			}
			curPkg = &usagepb.PackageInfo{
				PkgName: proto.String(result["package"]),
			}
		} else if m, result := parseutils.SubexpNames(userIDRE, line); m {
			if curPkg == nil {
				errs = append(errs, errors.New("found userId line before package line"))
				continue
			}
			uid, err := AppIDFromString(result["uid"])
			if err != nil {
				errs = append(errs, err)
			}
			curPkg.Uid = proto.Int32(uid)
		} else if m, result := parseutils.SubexpNames(packageDumpVersionCodeRE, line); m {
			if curPkg == nil {
				errs = append(errs, errors.New("found versionCode line before package line"))
				continue
			}
			vc, err := strconv.Atoi(result["versionCode"])
			if err != nil {
				errs = append(errs, fmt.Errorf("error getting version code from string: %v\n", err))
				continue
			}
			curPkg.VersionCode = proto.Int32(int32(vc))
		} else if m, result := parseutils.SubexpNames(packageDumpVersionNameRE, line); m {
			if curPkg == nil {
				errs = append(errs, errors.New("found versionName line before package line"))
				continue
			}
			curPkg.VersionName = proto.String(result["versionName"])
		} else if m, result := parseutils.SubexpNames(firstInstallTimeRE, line); m {
			if curPkg == nil {
				errs = append(errs, errors.New("found firstInstallTime line before package line"))
				continue
			}
			t, err := time.Parse(timeFormat, result["time"])
			if err != nil {
				errs = append(errs, err)
			}
			curPkg.FirstInstallTime = proto.Int64(t.UnixNano() / int64(time.Millisecond))
		} else if m, result := parseutils.SubexpNames(lastUpdateTimeRE, line); m {
			if curPkg == nil {
				errs = append(errs, errors.New("found lastUpdateTime line before package line"))
				continue
			}
			t, err := time.Parse(timeFormat, result["time"])
			if err != nil {
				errs = append(errs, err)
			}
			curPkg.LastUpdateTime = proto.Int64(t.UnixNano() / int64(time.Millisecond))
		}
	}

	if curPkg != nil {
		pkgs[curPkg.GetPkgName()] = curPkg
	}

	return pkgs, errs
}