// Copyright 2020 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package java // This file contains the module implementation for android_app_set. import ( "strconv" "strings" "github.com/google/blueprint/proptools" "android/soong/android" ) func init() { RegisterAppSetBuildComponents(android.InitRegistrationContext) } func RegisterAppSetBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("android_app_set", AndroidAppSetFactory) } type AndroidAppSetProperties struct { // APK Set path Set *string // Specifies that this app should be installed to the priv-app directory, // where the system will grant it additional privileges not available to // normal apps. Privileged *bool // APKs in this set use prerelease SDK version Prerelease *bool // Names of modules to be overridden. Listed modules can only be other apps // (in Make or Soong). Overrides []string // Path to the .prebuilt_info file of the prebuilt app. // In case of mainline modules, the .prebuilt_info file contains the build_id that was used // to generate the prebuilt. Prebuilt_info *string `android:"path"` } type AndroidAppSet struct { android.ModuleBase android.DefaultableModuleBase prebuilt android.Prebuilt properties AndroidAppSetProperties packedOutput android.WritablePath primaryOutput android.WritablePath apkcertsFile android.ModuleOutPath } func (as *AndroidAppSet) Name() string { return as.prebuilt.Name(as.ModuleBase.Name()) } func (as *AndroidAppSet) IsInstallable() bool { return true } func (as *AndroidAppSet) Prebuilt() *android.Prebuilt { return &as.prebuilt } func (as *AndroidAppSet) Privileged() bool { return Bool(as.properties.Privileged) } func (as *AndroidAppSet) OutputFile() android.Path { return as.primaryOutput } func (as *AndroidAppSet) PackedAdditionalOutputs() android.Path { return as.packedOutput } func (as *AndroidAppSet) APKCertsFile() android.Path { return as.apkcertsFile } var TargetCpuAbi = map[string]string{ "arm": "ARMEABI_V7A", "arm64": "ARM64_V8A", // TODO: use "RISCV64" when that is supported in bundles "riscv64": "ARM64_V8A", "x86": "X86", "x86_64": "X86_64", } func SupportedAbis(ctx android.ModuleContext, excludeNativeBridgeAbis bool) []string { abiName := func(targetIdx int, deviceArch string) string { if abi, found := TargetCpuAbi[deviceArch]; found { return abi } ctx.ModuleErrorf("Target %d has invalid Arch: %s", targetIdx, deviceArch) return "BAD_ABI" } var result []string for i, target := range ctx.Config().Targets[android.Android] { if target.NativeBridge == android.NativeBridgeEnabled && excludeNativeBridgeAbis { continue } result = append(result, abiName(i, target.Arch.ArchType.String())) } return result } type prebuiltInfoProps struct { baseModuleName string isPrebuilt bool prebuiltInfo *string } // Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file // with information about whether source or prebuilt of an apex was used during the build. func providePrebuiltInfo(ctx android.ModuleContext, p prebuiltInfoProps) { info := android.PrebuiltInfo{ Name: p.baseModuleName, Is_prebuilt: p.isPrebuilt, } // If Prebuilt_info information is available in the soong module definition, add it to prebuilt_info.json. if p.prebuiltInfo != nil { prebuiltInfoFile := android.PathForModuleSrc(ctx, *p.prebuiltInfo) info.Prebuilt_info_file_path = prebuiltInfoFile.String() } android.SetProvider(ctx, android.PrebuiltInfoProvider, info) } func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { as.packedOutput = android.PathForModuleOut(ctx, ctx.ModuleName()+".zip") as.primaryOutput = android.PathForModuleOut(ctx, as.BaseModuleName()+".apk") as.apkcertsFile = android.PathForModuleOut(ctx, "apkcerts.txt") // We are assuming here that the install file in the APK // set has `.apk` suffix. If it doesn't the build will fail. // APK sets containing APEX files are handled elsewhere. screenDensities := "all" if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 { screenDensities = strings.ToUpper(strings.Join(dpis, ",")) } // TODO(asmundak): handle locales. // TODO(asmundak): do we support device features ctx.Build(pctx, android.BuildParams{ Rule: extractMatchingApks, Description: "Extract APKs from APK set", Output: as.primaryOutput, ImplicitOutputs: android.WritablePaths{as.packedOutput, as.apkcertsFile}, Inputs: android.Paths{as.prebuilt.SingleSourcePath(ctx)}, Args: map[string]string{ "abis": strings.Join(SupportedAbis(ctx, false), ","), "allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)), "screen-densities": screenDensities, "sdk-version": ctx.Config().PlatformSdkVersion().String(), "skip-sdk-check": strconv.FormatBool(ctx.Config().IsEnvTrue("SOONG_SKIP_APPSET_SDK_CHECK")), "stem": as.BaseModuleName(), "apkcerts": as.apkcertsFile.String(), "partition": as.PartitionTag(ctx.DeviceConfig()), "zip": as.packedOutput.String(), }, }) var installDir android.InstallPath if as.Privileged() { installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName()) } else { installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName()) } ctx.InstallFileWithExtraFilesZip(installDir, as.BaseModuleName()+".apk", as.primaryOutput, as.packedOutput) providePrebuiltInfo(ctx, prebuiltInfoProps{ baseModuleName: as.BaseModuleName(), isPrebuilt: true, prebuiltInfo: as.properties.Prebuilt_info, }, ) } func (as *AndroidAppSet) InstallBypassMake() bool { return true } // android_app_set extracts a set of APKs based on the target device // configuration and installs this set as "split APKs". // The extracted set always contains an APK whose name is // _module_name_.apk and every split APK matching target device. // The extraction of the density-specific splits depends on // PRODUCT_AAPT_PREBUILT_DPI variable. If present (its value should // be a list density names: LDPI, MDPI, HDPI, etc.), only listed // splits will be extracted. Otherwise all density-specific splits // will be extracted. func AndroidAppSetFactory() android.Module { module := &AndroidAppSet{} module.AddProperties(&module.properties) InitJavaModule(module, android.DeviceSupported) android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set") return module }