Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9cd4216c authored by Jaewoong Jung's avatar Jaewoong Jung
Browse files

Output apkcerts file for android_app_set.

Soong and Make have no ways to figure out what splits will be outputted
from a given android_app_set, so it's impossible for them to provide
full PACKAGES.$(LOCAL_MODULE).CERTIFICATE entries, which are required to
build a final apkcerts.txt. This change makes extract_apks produce
apkcerts.txt files for each input modules instead. The Make-side
counterpart of this change merges all local apkcerts.txt into a final
one.

Bug: 160119159
Bug: 162464887
Test: main_test.go
Test: m apkcerts-list
Merged-In: I321e80fd636a955213761f56a3ac64bfe7f7f7c0
Change-Id: I321e80fd636a955213761f56a3ac64bfe7f7f7c0
parent 591e59e5
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -614,6 +614,33 @@ func (a *ModuleBase) ProductServicesSpecific() bool {
	return Bool(a.commonProperties.Product_services_specific)
}

func (m *ModuleBase) PartitionTag(config DeviceConfig) string {
	partition := "system"
	if m.SocSpecific() {
		// A SoC-specific module could be on the vendor partition at
		// "vendor" or the system partition at "system/vendor".
		if config.VendorPath() == "vendor" {
			partition = "vendor"
		}
	} else if m.DeviceSpecific() {
		// A device-specific module could be on the odm partition at
		// "odm", the vendor partition at "vendor/odm", or the system
		// partition at "system/vendor/odm".
		if config.OdmPath() == "odm" {
			partition = "odm"
		} else if strings.HasPrefix(config.OdmPath(), "vendor/") {
			partition = "vendor"
		}
	} else if m.ProductSpecific() {
		// A product-specific module could be on the product partition
		// at "product" or the system partition at "system/product".
		if config.ProductPath() == "product" {
			partition = "product"
		}
	}
	return partition
}

func (a *ModuleBase) Enabled() bool {
	if a.commonProperties.Enabled == nil {
		return !a.Os().DefaultDisabled
+32 −7
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import (
	"math"
	"os"
	"regexp"
	"sort"
	"strings"

	"github.com/golang/protobuf/proto"
@@ -355,7 +356,7 @@ type Zip2ZipWriter interface {

// Writes out selected entries, renaming them as needed
func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
	writer Zip2ZipWriter) error {
	writer Zip2ZipWriter, partition string) ([]string, error) {
	// Renaming rules:
	//  splits/MODULE-master.apk to STEM.apk
	// else
@@ -389,10 +390,11 @@ func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
	}

	entryOrigin := make(map[string]string) // output entry to input entry
	var apkcerts []string
	for _, apk := range selected.entries {
		apkFile, ok := apkSet.entries[apk]
		if !ok {
			return fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
			return nil, fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
		}
		inName := apkFile.Name
		outName, ok := renamer(inName)
@@ -405,10 +407,15 @@ func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
		}
		entryOrigin[outName] = inName
		if err := writer.CopyFrom(apkFile, outName); err != nil {
			return err
			return nil, err
		}
		if partition != "" {
			apkcerts = append(apkcerts, fmt.Sprintf(
				`name="%s" certificate="PRESIGNED" private_key=""`, outName))
		}
	return nil
	}
	sort.Strings(apkcerts)
	return apkcerts, nil
}

func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error {
@@ -433,6 +440,9 @@ var (
	}
	extractSingle = flag.Bool("extract-single", false,
		"extract a single target and output it uncompressed. only available for standalone apks and apexes.")
	apkcertsOutput = flag.String("apkcerts", "",
		"optional apkcerts.txt output file containing signing info of all outputted apks")
	partition = flag.String("partition", "", "partition string. required when -apkcerts is used.")
)

// Parse abi values
@@ -485,7 +495,8 @@ func (s screenDensityFlagValue) Set(densityList string) error {
func processArgs() {
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+
			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] <APK set>`)
			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] `+
			`[-apkcerts <apkcerts output file> -partition <partition>] <APK set>`)
		flag.PrintDefaults()
		os.Exit(2)
	}
@@ -498,7 +509,8 @@ func processArgs() {
		"allow prereleased")
	flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
	flag.Parse()
	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 || (targetConfig.stem == "" && !*extractSingle) {
	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 ||
		(targetConfig.stem == "" && !*extractSingle) || (*apkcertsOutput != "" && *partition == "") {
		flag.Usage()
	}
	targetConfig.sdkVersion = int32(*version)
@@ -536,7 +548,20 @@ func main() {
				log.Fatal(err)
			}
		}()
		err = apkSet.writeApks(sel, targetConfig, writer)
		apkcerts, err := apkSet.writeApks(sel, targetConfig, writer, *partition)
		if err == nil && *apkcertsOutput != "" {
			apkcertsFile, err := os.Create(*apkcertsOutput)
			if err != nil {
				log.Fatal(err)
			}
			defer apkcertsFile.Close()
			for _, a := range apkcerts {
				_, err = apkcertsFile.WriteString(a + "\n")
				if err != nil {
					log.Fatal(err)
				}
			}
		}
	}
	if err != nil {
		log.Fatal(err)
+27 −11
Original line number Diff line number Diff line
@@ -16,10 +16,11 @@ package main

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"reflect"
	"testing"

	"github.com/golang/protobuf/proto"

	bp "android/soong/cmd/extract_apks/bundle_proto"
	"android/soong/third_party/zip"
)
@@ -430,48 +431,63 @@ func (w testZip2ZipWriter) CopyFrom(file *zip.File, out string) error {
	return nil
}

type testCaseWriteZip struct {
type testCaseWriteApks struct {
	name       string
	moduleName string
	stem       string
	partition  string
	// what we write from what
	expected map[string]string
	expectedZipEntries map[string]string
	expectedApkcerts   []string
}

func TestWriteZip(t *testing.T) {
	testCases := []testCaseWriteZip{
func TestWriteApks(t *testing.T) {
	testCases := []testCaseWriteApks{
		{
			name:       "splits",
			moduleName: "mybase",
			stem:       "Foo",
			expected: map[string]string{
			partition:  "system",
			expectedZipEntries: map[string]string{
				"Foo.apk":       "splits/mybase-master.apk",
				"Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
			},
			expectedApkcerts: []string{
				`name="Foo-xhdpi.apk" certificate="PRESIGNED" private_key=""`,
				`name="Foo.apk" certificate="PRESIGNED" private_key=""`,
			},
		},
		{
			name:       "universal",
			moduleName: "base",
			stem:       "Bar",
			expected: map[string]string{
			partition:  "product",
			expectedZipEntries: map[string]string{
				"Bar.apk": "universal.apk",
			},
			expectedApkcerts: []string{
				`name="Bar.apk" certificate="PRESIGNED" private_key=""`,
			},
		},
	}
	for _, testCase := range testCases {
		apkSet := ApkSet{entries: make(map[string]*zip.File)}
		sel := SelectionResult{moduleName: testCase.moduleName}
		for _, in := range testCase.expected {
		for _, in := range testCase.expectedZipEntries {
			apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}}
			sel.entries = append(sel.entries, in)
		}
		writer := testZip2ZipWriter{make(map[string]string)}
		config := TargetConfig{stem: testCase.stem}
		if err := apkSet.writeApks(sel, config, writer); err != nil {
		apkcerts, err := apkSet.writeApks(sel, config, writer, testCase.partition)
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(testCase.expected, writer.entries) {
			t.Errorf("expected %v, got %v", testCase.expected, writer.entries)
		if !reflect.DeepEqual(testCase.expectedZipEntries, writer.entries) {
			t.Errorf("expected zip entries %v, got %v", testCase.expectedZipEntries, writer.entries)
		}
		if !reflect.DeepEqual(testCase.expectedApkcerts, apkcerts) {
			t.Errorf("expected apkcerts %v, got %v", testCase.expectedApkcerts, apkcerts)
		}
	}
}
+1 −0
Original line number Diff line number Diff line
@@ -607,6 +607,7 @@ func (apkSet *AndroidAppSet) AndroidMk() android.AndroidMkData {
					fmt.Fprintln(w, "LOCAL_PRIVILEGED_MODULE := true")
				}
				fmt.Fprintln(w, "LOCAL_APK_SET_MASTER_FILE := ", apkSet.masterFile)
				fmt.Fprintln(w, "LOCAL_APKCERTS_FILE := ", apkSet.apkcertsFile)
				fmt.Fprintln(w, "LOCAL_OVERRIDES_PACKAGES :=", strings.Join(apkSet.properties.Overrides, " "))
			},
		},
+9 −4
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ type AndroidAppSet struct {
	properties   AndroidAppSetProperties
	packedOutput android.WritablePath
	masterFile   string
	apkcertsFile android.ModuleOutPath
}

func (as *AndroidAppSet) Name() string {
@@ -107,6 +108,7 @@ func SupportedAbis(ctx android.ModuleContext) []string {

func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	as.packedOutput = android.PathForModuleOut(ctx, "extracted.zip")
	as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
	// We are assuming here that the master file in the APK
	// set has `.apk` suffix. If it doesn't the build will fail.
	// APK sets containing APEX files are handled elsewhere.
@@ -122,6 +124,7 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext)
			Rule:           extractMatchingApks,
			Description:    "Extract APKs from APK set",
			Output:         as.packedOutput,
			ImplicitOutput: as.apkcertsFile,
			Inputs:         android.Paths{as.prebuilt.SingleSourcePath(ctx)},
			Args: map[string]string{
				"abis":              strings.Join(SupportedAbis(ctx), ","),
@@ -129,6 +132,8 @@ func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext)
				"screen-densities":  screenDensities,
				"sdk-version":       ctx.Config().PlatformSdkVersion(),
				"stem":              ctx.ModuleName(),
				"apkcerts":          as.apkcertsFile.String(),
				"partition":         as.PartitionTag(ctx.DeviceConfig()),
			},
		})
	// TODO(asmundak): add this (it's wrong now, will cause copying extracted.zip)
Loading