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

Commit a5e5abc4 authored by Jaewoong Jung's avatar Jaewoong Jung
Browse files

Implement DPI variants in android_app_import.

Bug: 128610294
Test: app_test.go
Change-Id: Ie3e558bfdb40de6b0b9df95d3b373d08a4084d7b
parent ddda3ce2
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -50,6 +50,11 @@ func (p *Prebuilt) Name(name string) string {
	return "prebuilt_" + name
}

// The below source-related functions and the srcs, src fields are based on an assumption that
// prebuilt modules have a static source property at the moment. Currently there is only one
// exception, android_app_import, which chooses a source file depending on the product's DPI
// preference configs. We'll want to add native support for dynamic source cases if we end up having
// more modules like this.
func (p *Prebuilt) SingleSourcePath(ctx ModuleContext) Path {
	if p.srcs != nil {
		if len(*p.srcs) == 0 {
+79 −5
Original line number Diff line number Diff line
@@ -17,17 +17,20 @@ package java
// This file contains the module types for compiling Android apps.

import (
	"path/filepath"
	"strings"

	"github.com/google/blueprint"
	"github.com/google/blueprint/proptools"
	"path/filepath"
	"reflect"
	"strings"

	"android/soong/android"
	"android/soong/cc"
	"android/soong/tradefed"
)

var supportedDpis = [...]string{"Ldpi", "Mdpi", "Hdpi", "Xhdpi", "Xxhdpi", "Xxxhdpi"}
var dpiVariantsStruct reflect.Type

func init() {
	android.RegisterModuleType("android_app", AndroidAppFactory)
	android.RegisterModuleType("android_test", AndroidTestFactory)
@@ -35,6 +38,22 @@ func init() {
	android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
	android.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
	android.RegisterModuleType("android_app_import", AndroidAppImportFactory)

	// Dynamically construct a struct for the dpi_variants property in android_app_import.
	perDpiStruct := reflect.StructOf([]reflect.StructField{
		{
			Name: "Apk",
			Type: reflect.TypeOf((*string)(nil)),
		},
	})
	dpiVariantsFields := make([]reflect.StructField, len(supportedDpis))
	for i, dpi := range supportedDpis {
		dpiVariantsFields[i] = reflect.StructField{
			Name: string(dpi),
			Type: perDpiStruct,
		}
	}
	dpiVariantsStruct = reflect.StructOf(dpiVariantsFields)
}

// AndroidManifest.xml merging
@@ -635,6 +654,26 @@ type AndroidAppImportProperties struct {
	// A prebuilt apk to import
	Apk string

	// Per-DPI settings. This property makes it possible to specify a different source apk path for
	// each DPI.
	//
	// Example:
	//
	//     android_app_import {
	//         name: "example_import",
	//         apk: "prebuilts/example.apk",
	//         dpi_variants: {
	//             mdpi: {
	//                 apk: "prebuilts/example_mdpi.apk",
	//             },
	//             xhdpi: {
	//                 apk: "prebuilts/example_xhdpi.apk",
	//             },
	//         },
	//         certificate: "PRESIGNED",
	//     }
	Dpi_variants interface{}

	// The name of a certificate in the default certificate directory, blank to use the default
	// product certificate, or an android_app_certificate module name in the form ":module".
	Certificate *string
@@ -656,6 +695,41 @@ type AndroidAppImportProperties struct {
	Overrides []string
}

func getApkPathForDpi(dpiVariantsValue reflect.Value, dpi string) string {
	dpiField := dpiVariantsValue.FieldByName(proptools.FieldNameForProperty(dpi))
	if !dpiField.IsValid() {
		return ""
	}
	apkValue := dpiField.FieldByName("Apk").Elem()
	if apkValue.IsValid() {
		return apkValue.String()
	}
	return ""
}

// Chooses a source APK path to use based on the module's per-DPI settings and the product config.
func (a *AndroidAppImport) getSrcApkPath(ctx android.ModuleContext) string {
	config := ctx.Config()
	dpiVariantsValue := reflect.ValueOf(a.properties.Dpi_variants).Elem()
	if !dpiVariantsValue.IsValid() {
		return a.properties.Apk
	}
	// Match PRODUCT_AAPT_PREF_CONFIG first and then PRODUCT_AAPT_PREBUILT_DPI.
	if config.ProductAAPTPreferredConfig() != "" {
		if apk := getApkPathForDpi(dpiVariantsValue, config.ProductAAPTPreferredConfig()); apk != "" {
			return apk
		}
	}
	for _, dpi := range config.ProductAAPTPrebuiltDPI() {
		if apk := getApkPathForDpi(dpiVariantsValue, dpi); apk != "" {
			return apk
		}
	}

	// No match. Use the generic one.
	return a.properties.Apk
}

func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
	cert := android.SrcIsModule(String(a.properties.Certificate))
	if cert != "" {
@@ -701,10 +775,9 @@ func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext
	_, certificates := collectAppDeps(ctx)

	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
	// TODO: LOCAL_DPI_VARIANTS
	// TODO: LOCAL_PACKAGE_SPLITS

	srcApk := a.prebuilt.SingleSourcePath(ctx)
	srcApk := android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx))

	// TODO: Install or embed JNI libraries

@@ -754,6 +827,7 @@ func (a *AndroidAppImport) Name() string {
// android_app_import imports a prebuilt apk with additional processing specified in the module.
func AndroidAppImportFactory() android.Module {
	module := &AndroidAppImport{}
	module.properties.Dpi_variants = reflect.New(dpiVariantsStruct).Interface()
	module.AddProperties(&module.properties)
	module.AddProperties(&module.dexpreoptProperties)

+84 −3
Original line number Diff line number Diff line
@@ -15,15 +15,18 @@
package java

import (
	"android/soong/android"
	"android/soong/cc"

	"fmt"
	"path/filepath"
	"reflect"
	"regexp"
	"sort"
	"strings"
	"testing"

	"github.com/google/blueprint/proptools"

	"android/soong/android"
	"android/soong/cc"
)

var (
@@ -1052,3 +1055,81 @@ func TestAndroidAppImport_Presigned(t *testing.T) {
		t.Errorf("can't find aligning rule")
	}
}

func TestAndroidAppImport_DpiVariants(t *testing.T) {
	bp := `
		android_app_import {
			name: "foo",
			apk: "prebuilts/apk/app.apk",
			dpi_variants: {
				xhdpi: {
					apk: "prebuilts/apk/app_xhdpi.apk",
				},
				xxhdpi: {
					apk: "prebuilts/apk/app_xxhdpi.apk",
				},
			},
			certificate: "PRESIGNED",
			dex_preopt: {
				enabled: true,
			},
		}
		`
	testCases := []struct {
		name                string
		aaptPreferredConfig *string
		aaptPrebuiltDPI     []string
		expected            string
	}{
		{
			name:                "no preferred",
			aaptPreferredConfig: nil,
			aaptPrebuiltDPI:     []string{},
			expected:            "prebuilts/apk/app.apk",
		},
		{
			name:                "AAPTPreferredConfig matches",
			aaptPreferredConfig: proptools.StringPtr("xhdpi"),
			aaptPrebuiltDPI:     []string{"xxhdpi", "lhdpi"},
			expected:            "prebuilts/apk/app_xhdpi.apk",
		},
		{
			name:                "AAPTPrebuiltDPI matches",
			aaptPreferredConfig: proptools.StringPtr("mdpi"),
			aaptPrebuiltDPI:     []string{"xxhdpi", "xhdpi"},
			expected:            "prebuilts/apk/app_xxhdpi.apk",
		},
		{
			name:                "non-first AAPTPrebuiltDPI matches",
			aaptPreferredConfig: proptools.StringPtr("mdpi"),
			aaptPrebuiltDPI:     []string{"ldpi", "xhdpi"},
			expected:            "prebuilts/apk/app_xhdpi.apk",
		},
		{
			name:                "no matches",
			aaptPreferredConfig: proptools.StringPtr("mdpi"),
			aaptPrebuiltDPI:     []string{"ldpi", "xxxhdpi"},
			expected:            "prebuilts/apk/app.apk",
		},
	}

	jniRuleRe := regexp.MustCompile("^if \\(zipinfo (\\S+)")
	for _, test := range testCases {
		config := testConfig(nil)
		config.TestProductVariables.AAPTPreferredConfig = test.aaptPreferredConfig
		config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
		ctx := testAppContext(config, bp, nil)

		run(t, ctx, config)

		variant := ctx.ModuleForTests("foo", "android_common")
		jniRuleCommand := variant.Output("jnis-uncompressed/foo.apk").RuleParams.Command
		matches := jniRuleRe.FindStringSubmatch(jniRuleCommand)
		if len(matches) != 2 {
			t.Errorf("failed to extract the src apk path from %q", jniRuleCommand)
		}
		if test.expected != matches[1] {
			t.Errorf("wrong src apk, expected: %q got: %q", test.expected, matches[1])
		}
	}
}
+3 −1
Original line number Diff line number Diff line
@@ -165,6 +165,8 @@ func testContext(config android.Config, bp string,
		"prebuilts/sdk/Android.bp":                    []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),

		"prebuilts/apk/app.apk":        nil,
		"prebuilts/apk/app_xhdpi.apk":  nil,
		"prebuilts/apk/app_xxhdpi.apk": nil,

		// For framework-res, which is an implicit dependency for framework
		"AndroidManifest.xml":                        nil,