Implement extract_apks

Bug: 152319766
Test: manual and builtin
Change-Id: Ia15d66e86c7bcfd52f5b776173ca1665b68ff438
This commit is contained in:
Sasha Smundak 2020-05-06 21:23:08 -07:00
parent b9d65417c3
commit 7a894a6643
10 changed files with 5056 additions and 0 deletions

View File

@ -0,0 +1,21 @@
blueprint_go_binary {
name: "extract_apks",
srcs: ["main.go"],
deps: [
"android-archive-zip",
"golang-protobuf-proto",
"soong-cmd-extract_apks-proto",
],
testSrcs: ["main_test.go"]
}
bootstrap_go_package {
name: "soong-cmd-extract_apks-proto",
pkgPath: "android/soong/cmd/extract_apks/bundle_proto",
deps: ["golang-protobuf-proto"],
srcs: [
"bundle_proto/commands.pb.go",
"bundle_proto/config.pb.go",
"bundle_proto/targeting.pb.go",
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
// Messages describing APK Set's table of contents (toc.pb entry).
// Please be advised that the ultimate source is at
// https://github.com/google/bundletool/tree/master/src/main/proto
// so you have been warned.
syntax = "proto3";
package android.bundle;
import "config.proto";
import "targeting.proto";
option go_package = "android_bundle_proto";
option java_package = "com.android.bundle";
// Describes the output of the "build-apks" command.
message BuildApksResult {
// The package name of this app.
string package_name = 4;
// List of the created variants.
repeated Variant variant = 1;
// Metadata about BundleTool used to build the APKs.
Bundletool bundletool = 2;
// List of the created asset slices.
repeated AssetSliceSet asset_slice_set = 3;
// Information about local testing mode.
LocalTestingInfo local_testing_info = 5;
}
// Variant is a group of APKs that covers a part of the device configuration
// space. APKs from multiple variants are never combined on one device.
message Variant {
// Variant-level targeting.
// This targeting is fairly high-level and each APK has its own targeting as
// well.
VariantTargeting targeting = 1;
// Set of APKs, one set per module.
repeated ApkSet apk_set = 2;
// Number of the variant, starting at 0 (unless overridden).
// A device will receive APKs from the first variant that matches the device
// configuration, with higher variant numbers having priority over lower
// variant numbers.
uint32 variant_number = 3;
}
// Represents a module.
// For pre-L devices multiple modules (possibly all) may be merged into one.
message ApkSet {
ModuleMetadata module_metadata = 1;
// APKs.
repeated ApkDescription apk_description = 2;
}
message ModuleMetadata {
// Module name.
string name = 1;
// Indicates the delivery type (e.g. on-demand) of the module.
DeliveryType delivery_type = 6;
// Indicates whether this module is marked "instant".
bool is_instant = 3;
// Names of the modules that this module directly depends on.
// Each module implicitly depends on the base module.
repeated string dependencies = 4;
// The targeting that makes a conditional module installed.
// Relevant only for Split APKs.
ModuleTargeting targeting = 5;
// Deprecated. Please use delivery_type.
bool on_demand_deprecated = 2 [deprecated = true];
}
// Set of asset slices belonging to a single asset module.
message AssetSliceSet {
// Module level metadata.
AssetModuleMetadata asset_module_metadata = 1;
// Asset slices.
repeated ApkDescription apk_description = 2;
}
message AssetModuleMetadata {
// Module name.
string name = 1;
// Indicates the delivery type for persistent install.
DeliveryType delivery_type = 4;
// Metadata for instant installs.
InstantMetadata instant_metadata = 3;
// Deprecated. Use delivery_type.
bool on_demand_deprecated = 2 [deprecated = true];
}
message InstantMetadata {
// Indicates whether this module is marked "instant".
bool is_instant = 1;
// Indicates the delivery type for instant install.
DeliveryType delivery_type = 3;
// Deprecated. Use delivery_type.
bool on_demand_deprecated = 2 [deprecated = true];
}
enum DeliveryType {
UNKNOWN_DELIVERY_TYPE = 0;
INSTALL_TIME = 1;
ON_DEMAND = 2;
FAST_FOLLOW = 3;
}
message ApkDescription {
ApkTargeting targeting = 1;
// Path to the APK file.
// BEGIN-INTERNAL
// The path may be a blobkey if the proto is not constructed by bundletool.
// END-INTERNAL
string path = 2;
oneof apk_metadata_oneof_value {
// Set only for Split APKs.
SplitApkMetadata split_apk_metadata = 3;
// Set only for standalone APKs.
StandaloneApkMetadata standalone_apk_metadata = 4;
// Set only for Instant split APKs.
SplitApkMetadata instant_apk_metadata = 5;
// Set only for system APKs.
SystemApkMetadata system_apk_metadata = 6;
// Set only for asset slices.
SplitApkMetadata asset_slice_metadata = 7;
// Set only for APEX APKs.
ApexApkMetadata apex_apk_metadata = 8;
}
}
// Holds data specific to Split APKs.
message SplitApkMetadata {
string split_id = 1;
// Indicates whether this APK is the master split of the module.
bool is_master_split = 2;
}
// Holds data specific to Standalone APKs.
message StandaloneApkMetadata {
// Names of the modules fused in this standalone APK.
repeated string fused_module_name = 1;
reserved 2;
}
// Holds data specific to system APKs.
message SystemApkMetadata {
// Names of the modules fused in this system APK.
repeated string fused_module_name = 1;
enum SystemApkType {
UNSPECIFIED_VALUE = 0;
// Uncompressed APK for system image.
SYSTEM = 1;
// Stub APK for compressed APK in the system image
// (contains only android manifest).
SYSTEM_STUB = 2;
// Compressed APK for system image.
SYSTEM_COMPRESSED = 3;
}
// Indicates whether the APK is uncompressed system APK, stub APK or
// compressed system APK.
SystemApkType system_apk_type = 2;
}
// Holds data specific to APEX APKs.
message ApexApkMetadata {
// Configuration for processing of APKs embedded in an APEX image.
repeated ApexEmbeddedApkConfig apex_embedded_apk_config = 1;
}
message LocalTestingInfo {
// Indicates if the bundle is built in local testing mode.
bool enabled = 1;
// The local testing path, as specified in the base manifest.
// This refers to the relative path on the external directory of the app where
// APKs will be pushed for local testing.
// Set only if local testing is enabled.
string local_testing_path = 2;
}

View File

@ -0,0 +1,952 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: config.proto
package android_bundle_proto
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type BundleConfig_BundleType int32
const (
BundleConfig_REGULAR BundleConfig_BundleType = 0
BundleConfig_APEX BundleConfig_BundleType = 1
BundleConfig_ASSET_ONLY BundleConfig_BundleType = 2
)
var BundleConfig_BundleType_name = map[int32]string{
0: "REGULAR",
1: "APEX",
2: "ASSET_ONLY",
}
var BundleConfig_BundleType_value = map[string]int32{
"REGULAR": 0,
"APEX": 1,
"ASSET_ONLY": 2,
}
func (x BundleConfig_BundleType) String() string {
return proto.EnumName(BundleConfig_BundleType_name, int32(x))
}
func (BundleConfig_BundleType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{0, 0}
}
type SplitDimension_Value int32
const (
SplitDimension_UNSPECIFIED_VALUE SplitDimension_Value = 0
SplitDimension_ABI SplitDimension_Value = 1
SplitDimension_SCREEN_DENSITY SplitDimension_Value = 2
SplitDimension_LANGUAGE SplitDimension_Value = 3
SplitDimension_TEXTURE_COMPRESSION_FORMAT SplitDimension_Value = 4
// BEGIN-INTERNAL
SplitDimension_GRAPHICS_API SplitDimension_Value = 5
)
var SplitDimension_Value_name = map[int32]string{
0: "UNSPECIFIED_VALUE",
1: "ABI",
2: "SCREEN_DENSITY",
3: "LANGUAGE",
4: "TEXTURE_COMPRESSION_FORMAT",
5: "GRAPHICS_API",
}
var SplitDimension_Value_value = map[string]int32{
"UNSPECIFIED_VALUE": 0,
"ABI": 1,
"SCREEN_DENSITY": 2,
"LANGUAGE": 3,
"TEXTURE_COMPRESSION_FORMAT": 4,
"GRAPHICS_API": 5,
}
func (x SplitDimension_Value) String() string {
return proto.EnumName(SplitDimension_Value_name, int32(x))
}
func (SplitDimension_Value) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{9, 0}
}
type BundleConfig struct {
Bundletool *Bundletool `protobuf:"bytes,1,opt,name=bundletool,proto3" json:"bundletool,omitempty"`
Optimizations *Optimizations `protobuf:"bytes,2,opt,name=optimizations,proto3" json:"optimizations,omitempty"`
Compression *Compression `protobuf:"bytes,3,opt,name=compression,proto3" json:"compression,omitempty"`
// Resources to be always kept in the master split.
MasterResources *MasterResources `protobuf:"bytes,4,opt,name=master_resources,json=masterResources,proto3" json:"master_resources,omitempty"`
ApexConfig *ApexConfig `protobuf:"bytes,5,opt,name=apex_config,json=apexConfig,proto3" json:"apex_config,omitempty"`
// APKs to be signed with the same key as generated APKs.
UnsignedEmbeddedApkConfig []*UnsignedEmbeddedApkConfig `protobuf:"bytes,6,rep,name=unsigned_embedded_apk_config,json=unsignedEmbeddedApkConfig,proto3" json:"unsigned_embedded_apk_config,omitempty"`
AssetModulesConfig *AssetModulesConfig `protobuf:"bytes,7,opt,name=asset_modules_config,json=assetModulesConfig,proto3" json:"asset_modules_config,omitempty"`
Type BundleConfig_BundleType `protobuf:"varint,8,opt,name=type,proto3,enum=android.bundle.BundleConfig_BundleType" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BundleConfig) Reset() { *m = BundleConfig{} }
func (m *BundleConfig) String() string { return proto.CompactTextString(m) }
func (*BundleConfig) ProtoMessage() {}
func (*BundleConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{0}
}
func (m *BundleConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BundleConfig.Unmarshal(m, b)
}
func (m *BundleConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BundleConfig.Marshal(b, m, deterministic)
}
func (m *BundleConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_BundleConfig.Merge(m, src)
}
func (m *BundleConfig) XXX_Size() int {
return xxx_messageInfo_BundleConfig.Size(m)
}
func (m *BundleConfig) XXX_DiscardUnknown() {
xxx_messageInfo_BundleConfig.DiscardUnknown(m)
}
var xxx_messageInfo_BundleConfig proto.InternalMessageInfo
func (m *BundleConfig) GetBundletool() *Bundletool {
if m != nil {
return m.Bundletool
}
return nil
}
func (m *BundleConfig) GetOptimizations() *Optimizations {
if m != nil {
return m.Optimizations
}
return nil
}
func (m *BundleConfig) GetCompression() *Compression {
if m != nil {
return m.Compression
}
return nil
}
func (m *BundleConfig) GetMasterResources() *MasterResources {
if m != nil {
return m.MasterResources
}
return nil
}
func (m *BundleConfig) GetApexConfig() *ApexConfig {
if m != nil {
return m.ApexConfig
}
return nil
}
func (m *BundleConfig) GetUnsignedEmbeddedApkConfig() []*UnsignedEmbeddedApkConfig {
if m != nil {
return m.UnsignedEmbeddedApkConfig
}
return nil
}
func (m *BundleConfig) GetAssetModulesConfig() *AssetModulesConfig {
if m != nil {
return m.AssetModulesConfig
}
return nil
}
func (m *BundleConfig) GetType() BundleConfig_BundleType {
if m != nil {
return m.Type
}
return BundleConfig_REGULAR
}
type Bundletool struct {
// Version of BundleTool used to build the Bundle.
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Bundletool) Reset() { *m = Bundletool{} }
func (m *Bundletool) String() string { return proto.CompactTextString(m) }
func (*Bundletool) ProtoMessage() {}
func (*Bundletool) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{1}
}
func (m *Bundletool) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Bundletool.Unmarshal(m, b)
}
func (m *Bundletool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Bundletool.Marshal(b, m, deterministic)
}
func (m *Bundletool) XXX_Merge(src proto.Message) {
xxx_messageInfo_Bundletool.Merge(m, src)
}
func (m *Bundletool) XXX_Size() int {
return xxx_messageInfo_Bundletool.Size(m)
}
func (m *Bundletool) XXX_DiscardUnknown() {
xxx_messageInfo_Bundletool.DiscardUnknown(m)
}
var xxx_messageInfo_Bundletool proto.InternalMessageInfo
func (m *Bundletool) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
type Compression struct {
// Glob matching the list of files to leave uncompressed in the APKs.
// The matching is done against the path of files in the APK, thus excluding
// the name of the modules, and using forward slash ("/") as a name separator.
// Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
UncompressedGlob []string `protobuf:"bytes,1,rep,name=uncompressed_glob,json=uncompressedGlob,proto3" json:"uncompressed_glob,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Compression) Reset() { *m = Compression{} }
func (m *Compression) String() string { return proto.CompactTextString(m) }
func (*Compression) ProtoMessage() {}
func (*Compression) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{2}
}
func (m *Compression) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Compression.Unmarshal(m, b)
}
func (m *Compression) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Compression.Marshal(b, m, deterministic)
}
func (m *Compression) XXX_Merge(src proto.Message) {
xxx_messageInfo_Compression.Merge(m, src)
}
func (m *Compression) XXX_Size() int {
return xxx_messageInfo_Compression.Size(m)
}
func (m *Compression) XXX_DiscardUnknown() {
xxx_messageInfo_Compression.DiscardUnknown(m)
}
var xxx_messageInfo_Compression proto.InternalMessageInfo
func (m *Compression) GetUncompressedGlob() []string {
if m != nil {
return m.UncompressedGlob
}
return nil
}
// Resources to keep in the master split.
type MasterResources struct {
// Resource IDs to be kept in master split.
ResourceIds []int32 `protobuf:"varint,1,rep,packed,name=resource_ids,json=resourceIds,proto3" json:"resource_ids,omitempty"`
// Resource names to be kept in master split.
ResourceNames []string `protobuf:"bytes,2,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MasterResources) Reset() { *m = MasterResources{} }
func (m *MasterResources) String() string { return proto.CompactTextString(m) }
func (*MasterResources) ProtoMessage() {}
func (*MasterResources) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{3}
}
func (m *MasterResources) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MasterResources.Unmarshal(m, b)
}
func (m *MasterResources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MasterResources.Marshal(b, m, deterministic)
}
func (m *MasterResources) XXX_Merge(src proto.Message) {
xxx_messageInfo_MasterResources.Merge(m, src)
}
func (m *MasterResources) XXX_Size() int {
return xxx_messageInfo_MasterResources.Size(m)
}
func (m *MasterResources) XXX_DiscardUnknown() {
xxx_messageInfo_MasterResources.DiscardUnknown(m)
}
var xxx_messageInfo_MasterResources proto.InternalMessageInfo
func (m *MasterResources) GetResourceIds() []int32 {
if m != nil {
return m.ResourceIds
}
return nil
}
func (m *MasterResources) GetResourceNames() []string {
if m != nil {
return m.ResourceNames
}
return nil
}
type Optimizations struct {
SplitsConfig *SplitsConfig `protobuf:"bytes,1,opt,name=splits_config,json=splitsConfig,proto3" json:"splits_config,omitempty"`
// This is for uncompressing native libraries on M+ devices (L+ devices on
// instant apps).
UncompressNativeLibraries *UncompressNativeLibraries `protobuf:"bytes,2,opt,name=uncompress_native_libraries,json=uncompressNativeLibraries,proto3" json:"uncompress_native_libraries,omitempty"`
// This is for uncompressing dex files on P+ devices.
UncompressDexFiles *UncompressDexFiles `protobuf:"bytes,3,opt,name=uncompress_dex_files,json=uncompressDexFiles,proto3" json:"uncompress_dex_files,omitempty"`
// Configuration for the generation of standalone APKs.
// If no StandaloneConfig is set, the configuration is inherited from
// splits_config.
StandaloneConfig *StandaloneConfig `protobuf:"bytes,4,opt,name=standalone_config,json=standaloneConfig,proto3" json:"standalone_config,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Optimizations) Reset() { *m = Optimizations{} }
func (m *Optimizations) String() string { return proto.CompactTextString(m) }
func (*Optimizations) ProtoMessage() {}
func (*Optimizations) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{4}
}
func (m *Optimizations) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Optimizations.Unmarshal(m, b)
}
func (m *Optimizations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Optimizations.Marshal(b, m, deterministic)
}
func (m *Optimizations) XXX_Merge(src proto.Message) {
xxx_messageInfo_Optimizations.Merge(m, src)
}
func (m *Optimizations) XXX_Size() int {
return xxx_messageInfo_Optimizations.Size(m)
}
func (m *Optimizations) XXX_DiscardUnknown() {
xxx_messageInfo_Optimizations.DiscardUnknown(m)
}
var xxx_messageInfo_Optimizations proto.InternalMessageInfo
func (m *Optimizations) GetSplitsConfig() *SplitsConfig {
if m != nil {
return m.SplitsConfig
}
return nil
}
func (m *Optimizations) GetUncompressNativeLibraries() *UncompressNativeLibraries {
if m != nil {
return m.UncompressNativeLibraries
}
return nil
}
func (m *Optimizations) GetUncompressDexFiles() *UncompressDexFiles {
if m != nil {
return m.UncompressDexFiles
}
return nil
}
func (m *Optimizations) GetStandaloneConfig() *StandaloneConfig {
if m != nil {
return m.StandaloneConfig
}
return nil
}
type UncompressNativeLibraries struct {
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UncompressNativeLibraries) Reset() { *m = UncompressNativeLibraries{} }
func (m *UncompressNativeLibraries) String() string { return proto.CompactTextString(m) }
func (*UncompressNativeLibraries) ProtoMessage() {}
func (*UncompressNativeLibraries) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{5}
}
func (m *UncompressNativeLibraries) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UncompressNativeLibraries.Unmarshal(m, b)
}
func (m *UncompressNativeLibraries) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UncompressNativeLibraries.Marshal(b, m, deterministic)
}
func (m *UncompressNativeLibraries) XXX_Merge(src proto.Message) {
xxx_messageInfo_UncompressNativeLibraries.Merge(m, src)
}
func (m *UncompressNativeLibraries) XXX_Size() int {
return xxx_messageInfo_UncompressNativeLibraries.Size(m)
}
func (m *UncompressNativeLibraries) XXX_DiscardUnknown() {
xxx_messageInfo_UncompressNativeLibraries.DiscardUnknown(m)
}
var xxx_messageInfo_UncompressNativeLibraries proto.InternalMessageInfo
func (m *UncompressNativeLibraries) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
type UncompressDexFiles struct {
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UncompressDexFiles) Reset() { *m = UncompressDexFiles{} }
func (m *UncompressDexFiles) String() string { return proto.CompactTextString(m) }
func (*UncompressDexFiles) ProtoMessage() {}
func (*UncompressDexFiles) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{6}
}
func (m *UncompressDexFiles) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UncompressDexFiles.Unmarshal(m, b)
}
func (m *UncompressDexFiles) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UncompressDexFiles.Marshal(b, m, deterministic)
}
func (m *UncompressDexFiles) XXX_Merge(src proto.Message) {
xxx_messageInfo_UncompressDexFiles.Merge(m, src)
}
func (m *UncompressDexFiles) XXX_Size() int {
return xxx_messageInfo_UncompressDexFiles.Size(m)
}
func (m *UncompressDexFiles) XXX_DiscardUnknown() {
xxx_messageInfo_UncompressDexFiles.DiscardUnknown(m)
}
var xxx_messageInfo_UncompressDexFiles proto.InternalMessageInfo
func (m *UncompressDexFiles) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
// Optimization configuration used to generate Split APKs.
type SplitsConfig struct {
SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SplitsConfig) Reset() { *m = SplitsConfig{} }
func (m *SplitsConfig) String() string { return proto.CompactTextString(m) }
func (*SplitsConfig) ProtoMessage() {}
func (*SplitsConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{7}
}
func (m *SplitsConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SplitsConfig.Unmarshal(m, b)
}
func (m *SplitsConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SplitsConfig.Marshal(b, m, deterministic)
}
func (m *SplitsConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_SplitsConfig.Merge(m, src)
}
func (m *SplitsConfig) XXX_Size() int {
return xxx_messageInfo_SplitsConfig.Size(m)
}
func (m *SplitsConfig) XXX_DiscardUnknown() {
xxx_messageInfo_SplitsConfig.DiscardUnknown(m)
}
var xxx_messageInfo_SplitsConfig proto.InternalMessageInfo
func (m *SplitsConfig) GetSplitDimension() []*SplitDimension {
if m != nil {
return m.SplitDimension
}
return nil
}
// Optimization configuration used to generate Standalone APKs.
type StandaloneConfig struct {
// Device targeting dimensions to shard.
SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
// Whether 64 bit libraries should be stripped from Standalone APKs.
Strip_64BitLibraries bool `protobuf:"varint,2,opt,name=strip_64_bit_libraries,json=strip64BitLibraries,proto3" json:"strip_64_bit_libraries,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StandaloneConfig) Reset() { *m = StandaloneConfig{} }
func (m *StandaloneConfig) String() string { return proto.CompactTextString(m) }
func (*StandaloneConfig) ProtoMessage() {}
func (*StandaloneConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{8}
}
func (m *StandaloneConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StandaloneConfig.Unmarshal(m, b)
}
func (m *StandaloneConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_StandaloneConfig.Marshal(b, m, deterministic)
}
func (m *StandaloneConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_StandaloneConfig.Merge(m, src)
}
func (m *StandaloneConfig) XXX_Size() int {
return xxx_messageInfo_StandaloneConfig.Size(m)
}
func (m *StandaloneConfig) XXX_DiscardUnknown() {
xxx_messageInfo_StandaloneConfig.DiscardUnknown(m)
}
var xxx_messageInfo_StandaloneConfig proto.InternalMessageInfo
func (m *StandaloneConfig) GetSplitDimension() []*SplitDimension {
if m != nil {
return m.SplitDimension
}
return nil
}
func (m *StandaloneConfig) GetStrip_64BitLibraries() bool {
if m != nil {
return m.Strip_64BitLibraries
}
return false
}
type SplitDimension struct {
Value SplitDimension_Value `protobuf:"varint,1,opt,name=value,proto3,enum=android.bundle.SplitDimension_Value" json:"value,omitempty"`
// If set to 'true', indicates that APKs should *not* be split by this
// dimension.
Negate bool `protobuf:"varint,2,opt,name=negate,proto3" json:"negate,omitempty"`
// Optional transformation to be applied to asset directories where
// the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
SuffixStripping *SuffixStripping `protobuf:"bytes,3,opt,name=suffix_stripping,json=suffixStripping,proto3" json:"suffix_stripping,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SplitDimension) Reset() { *m = SplitDimension{} }
func (m *SplitDimension) String() string { return proto.CompactTextString(m) }
func (*SplitDimension) ProtoMessage() {}
func (*SplitDimension) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{9}
}
func (m *SplitDimension) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SplitDimension.Unmarshal(m, b)
}
func (m *SplitDimension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SplitDimension.Marshal(b, m, deterministic)
}
func (m *SplitDimension) XXX_Merge(src proto.Message) {
xxx_messageInfo_SplitDimension.Merge(m, src)
}
func (m *SplitDimension) XXX_Size() int {
return xxx_messageInfo_SplitDimension.Size(m)
}
func (m *SplitDimension) XXX_DiscardUnknown() {
xxx_messageInfo_SplitDimension.DiscardUnknown(m)
}
var xxx_messageInfo_SplitDimension proto.InternalMessageInfo
func (m *SplitDimension) GetValue() SplitDimension_Value {
if m != nil {
return m.Value
}
return SplitDimension_UNSPECIFIED_VALUE
}
func (m *SplitDimension) GetNegate() bool {
if m != nil {
return m.Negate
}
return false
}
func (m *SplitDimension) GetSuffixStripping() *SuffixStripping {
if m != nil {
return m.SuffixStripping
}
return nil
}
type SuffixStripping struct {
// If set to 'true', indicates that the targeting suffix should be removed
// from assets paths for this dimension when splits (or asset slices) are
// generated.
// This only applies to assets.
// For example a folder with path "assets/level1_textures#tcf_etc1"
// would be outputted to "assets/level1_textures". File contents are
// unchanged.
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
// The default suffix to be used for the cases where separate slices can't
// be generated for this dimension. In the case of standalone/universal APKs
// generation, stripping the suffix can lead to file name collisions. This
// default suffix defines the directories to retain. The others are
// discarded: standalone/universal APKs will contain only directories
// targeted at this value for the dimension.
//
// If not set or empty, the fallback directory in each directory group will be
// used (for example, if both "assets/level1_textures#tcf_etc1" and
// "assets/level1_textures" are present and the default suffix is empty,
// then only "assets/level1_textures" will be used).
DefaultSuffix string `protobuf:"bytes,2,opt,name=default_suffix,json=defaultSuffix,proto3" json:"default_suffix,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SuffixStripping) Reset() { *m = SuffixStripping{} }
func (m *SuffixStripping) String() string { return proto.CompactTextString(m) }
func (*SuffixStripping) ProtoMessage() {}
func (*SuffixStripping) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{10}
}
func (m *SuffixStripping) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SuffixStripping.Unmarshal(m, b)
}
func (m *SuffixStripping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SuffixStripping.Marshal(b, m, deterministic)
}
func (m *SuffixStripping) XXX_Merge(src proto.Message) {
xxx_messageInfo_SuffixStripping.Merge(m, src)
}
func (m *SuffixStripping) XXX_Size() int {
return xxx_messageInfo_SuffixStripping.Size(m)
}
func (m *SuffixStripping) XXX_DiscardUnknown() {
xxx_messageInfo_SuffixStripping.DiscardUnknown(m)
}
var xxx_messageInfo_SuffixStripping proto.InternalMessageInfo
func (m *SuffixStripping) GetEnabled() bool {
if m != nil {
return m.Enabled
}
return false
}
func (m *SuffixStripping) GetDefaultSuffix() string {
if m != nil {
return m.DefaultSuffix
}
return ""
}
// Configuration for processing APEX bundles.
// https://source.android.com/devices/tech/ota/apex
type ApexConfig struct {
// Configuration for processing of APKs embedded in an APEX image.
ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ApexConfig) Reset() { *m = ApexConfig{} }
func (m *ApexConfig) String() string { return proto.CompactTextString(m) }
func (*ApexConfig) ProtoMessage() {}
func (*ApexConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{11}
}
func (m *ApexConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ApexConfig.Unmarshal(m, b)
}
func (m *ApexConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ApexConfig.Marshal(b, m, deterministic)
}
func (m *ApexConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_ApexConfig.Merge(m, src)
}
func (m *ApexConfig) XXX_Size() int {
return xxx_messageInfo_ApexConfig.Size(m)
}
func (m *ApexConfig) XXX_DiscardUnknown() {
xxx_messageInfo_ApexConfig.DiscardUnknown(m)
}
var xxx_messageInfo_ApexConfig proto.InternalMessageInfo
func (m *ApexConfig) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
if m != nil {
return m.ApexEmbeddedApkConfig
}
return nil
}
type ApexEmbeddedApkConfig struct {
// Android package name of the APK.
PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
// Path to the APK within the APEX system image.
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ApexEmbeddedApkConfig) Reset() { *m = ApexEmbeddedApkConfig{} }
func (m *ApexEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
func (*ApexEmbeddedApkConfig) ProtoMessage() {}
func (*ApexEmbeddedApkConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{12}
}
func (m *ApexEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ApexEmbeddedApkConfig.Unmarshal(m, b)
}
func (m *ApexEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ApexEmbeddedApkConfig.Marshal(b, m, deterministic)
}
func (m *ApexEmbeddedApkConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_ApexEmbeddedApkConfig.Merge(m, src)
}
func (m *ApexEmbeddedApkConfig) XXX_Size() int {
return xxx_messageInfo_ApexEmbeddedApkConfig.Size(m)
}
func (m *ApexEmbeddedApkConfig) XXX_DiscardUnknown() {
xxx_messageInfo_ApexEmbeddedApkConfig.DiscardUnknown(m)
}
var xxx_messageInfo_ApexEmbeddedApkConfig proto.InternalMessageInfo
func (m *ApexEmbeddedApkConfig) GetPackageName() string {
if m != nil {
return m.PackageName
}
return ""
}
func (m *ApexEmbeddedApkConfig) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
type UnsignedEmbeddedApkConfig struct {
// Path to the APK inside the module (e.g. if the path inside the bundle
// is split/assets/example.apk, this will be assets/example.apk).
Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UnsignedEmbeddedApkConfig) Reset() { *m = UnsignedEmbeddedApkConfig{} }
func (m *UnsignedEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
func (*UnsignedEmbeddedApkConfig) ProtoMessage() {}
func (*UnsignedEmbeddedApkConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{13}
}
func (m *UnsignedEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UnsignedEmbeddedApkConfig.Unmarshal(m, b)
}
func (m *UnsignedEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UnsignedEmbeddedApkConfig.Marshal(b, m, deterministic)
}
func (m *UnsignedEmbeddedApkConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_UnsignedEmbeddedApkConfig.Merge(m, src)
}
func (m *UnsignedEmbeddedApkConfig) XXX_Size() int {
return xxx_messageInfo_UnsignedEmbeddedApkConfig.Size(m)
}
func (m *UnsignedEmbeddedApkConfig) XXX_DiscardUnknown() {
xxx_messageInfo_UnsignedEmbeddedApkConfig.DiscardUnknown(m)
}
var xxx_messageInfo_UnsignedEmbeddedApkConfig proto.InternalMessageInfo
func (m *UnsignedEmbeddedApkConfig) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
type AssetModulesConfig struct {
// App versionCodes that will be updated with these asset modules.
// Only relevant for asset-only bundles.
AppVersion []int64 `protobuf:"varint,1,rep,packed,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
// Version tag for the asset upload.
// Only relevant for asset-only bundles.
AssetVersionTag string `protobuf:"bytes,2,opt,name=asset_version_tag,json=assetVersionTag,proto3" json:"asset_version_tag,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AssetModulesConfig) Reset() { *m = AssetModulesConfig{} }
func (m *AssetModulesConfig) String() string { return proto.CompactTextString(m) }
func (*AssetModulesConfig) ProtoMessage() {}
func (*AssetModulesConfig) Descriptor() ([]byte, []int) {
return fileDescriptor_3eaf2c85e69e9ea4, []int{14}
}
func (m *AssetModulesConfig) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AssetModulesConfig.Unmarshal(m, b)
}
func (m *AssetModulesConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AssetModulesConfig.Marshal(b, m, deterministic)
}
func (m *AssetModulesConfig) XXX_Merge(src proto.Message) {
xxx_messageInfo_AssetModulesConfig.Merge(m, src)
}
func (m *AssetModulesConfig) XXX_Size() int {
return xxx_messageInfo_AssetModulesConfig.Size(m)
}
func (m *AssetModulesConfig) XXX_DiscardUnknown() {
xxx_messageInfo_AssetModulesConfig.DiscardUnknown(m)
}
var xxx_messageInfo_AssetModulesConfig proto.InternalMessageInfo
func (m *AssetModulesConfig) GetAppVersion() []int64 {
if m != nil {
return m.AppVersion
}
return nil
}
func (m *AssetModulesConfig) GetAssetVersionTag() string {
if m != nil {
return m.AssetVersionTag
}
return ""
}
func init() {
proto.RegisterEnum("android.bundle.BundleConfig_BundleType", BundleConfig_BundleType_name, BundleConfig_BundleType_value)
proto.RegisterEnum("android.bundle.SplitDimension_Value", SplitDimension_Value_name, SplitDimension_Value_value)
proto.RegisterType((*BundleConfig)(nil), "android.bundle.BundleConfig")
proto.RegisterType((*Bundletool)(nil), "android.bundle.Bundletool")
proto.RegisterType((*Compression)(nil), "android.bundle.Compression")
proto.RegisterType((*MasterResources)(nil), "android.bundle.MasterResources")
proto.RegisterType((*Optimizations)(nil), "android.bundle.Optimizations")
proto.RegisterType((*UncompressNativeLibraries)(nil), "android.bundle.UncompressNativeLibraries")
proto.RegisterType((*UncompressDexFiles)(nil), "android.bundle.UncompressDexFiles")
proto.RegisterType((*SplitsConfig)(nil), "android.bundle.SplitsConfig")
proto.RegisterType((*StandaloneConfig)(nil), "android.bundle.StandaloneConfig")
proto.RegisterType((*SplitDimension)(nil), "android.bundle.SplitDimension")
proto.RegisterType((*SuffixStripping)(nil), "android.bundle.SuffixStripping")
proto.RegisterType((*ApexConfig)(nil), "android.bundle.ApexConfig")
proto.RegisterType((*ApexEmbeddedApkConfig)(nil), "android.bundle.ApexEmbeddedApkConfig")
proto.RegisterType((*UnsignedEmbeddedApkConfig)(nil), "android.bundle.UnsignedEmbeddedApkConfig")
proto.RegisterType((*AssetModulesConfig)(nil), "android.bundle.AssetModulesConfig")
}
func init() {
proto.RegisterFile("config.proto", fileDescriptor_3eaf2c85e69e9ea4)
}
var fileDescriptor_3eaf2c85e69e9ea4 = []byte{
// 1001 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdb, 0x6e, 0xdb, 0x46,
0x10, 0x0d, 0x75, 0xb1, 0xe5, 0x91, 0x2c, 0xd1, 0xdb, 0x38, 0x50, 0x2e, 0x4d, 0x5c, 0xa2, 0x41,
0xdd, 0xb4, 0x50, 0x01, 0x3b, 0xcd, 0x83, 0x83, 0x3e, 0xd0, 0x32, 0xad, 0x2a, 0xd0, 0x0d, 0x4b,
0xc9, 0x4d, 0x5a, 0xa0, 0x8b, 0x95, 0xb8, 0x52, 0xb7, 0xa6, 0x48, 0x82, 0x4b, 0x1a, 0x4a, 0xfb,
0x09, 0x7d, 0xe9, 0x8f, 0xf4, 0xa7, 0xfa, 0x25, 0x05, 0x97, 0xa4, 0x2c, 0x51, 0x52, 0x9e, 0xfa,
0x24, 0xce, 0xec, 0x39, 0xb3, 0x3b, 0xb3, 0x67, 0x67, 0x04, 0x95, 0x89, 0xeb, 0x4c, 0xf9, 0xac,
0xe1, 0xf9, 0x6e, 0xe0, 0xa2, 0x2a, 0x75, 0x2c, 0xdf, 0xe5, 0x56, 0x63, 0x1c, 0x3a, 0x96, 0xcd,
0xb4, 0xbf, 0x8a, 0x50, 0xb9, 0x94, 0x9f, 0x4d, 0x09, 0x43, 0x17, 0x00, 0xf1, 0x52, 0xe0, 0xba,
0x76, 0x5d, 0x39, 0x51, 0x4e, 0xcb, 0x67, 0x4f, 0x1a, 0xeb, 0xac, 0xc6, 0xe5, 0x12, 0x81, 0x57,
0xd0, 0xa8, 0x09, 0x87, 0xae, 0x17, 0xf0, 0x39, 0xff, 0x83, 0x06, 0xdc, 0x75, 0x44, 0x3d, 0x27,
0xe9, 0x9f, 0x67, 0xe9, 0xfd, 0x55, 0x10, 0x5e, 0xe7, 0xa0, 0x1f, 0xa0, 0x3c, 0x71, 0xe7, 0x9e,
0xcf, 0x84, 0xe0, 0xae, 0x53, 0xcf, 0xcb, 0x10, 0x4f, 0xb3, 0x21, 0x9a, 0xf7, 0x10, 0xbc, 0x8a,
0x47, 0xef, 0x40, 0x9d, 0x53, 0x11, 0x30, 0x9f, 0xf8, 0x4c, 0xb8, 0xa1, 0x3f, 0x61, 0xa2, 0x5e,
0x90, 0x31, 0x5e, 0x64, 0x63, 0x74, 0x25, 0x0e, 0xa7, 0x30, 0x5c, 0x9b, 0xaf, 0x3b, 0xd0, 0x5b,
0x28, 0x53, 0x8f, 0x2d, 0x48, 0x5c, 0xc1, 0x7a, 0x71, 0x7b, 0x31, 0x74, 0x8f, 0x2d, 0xe2, 0xe2,
0x61, 0xa0, 0xcb, 0x6f, 0xf4, 0x3b, 0x3c, 0x0b, 0x1d, 0xc1, 0x67, 0x0e, 0xb3, 0x08, 0x9b, 0x8f,
0x99, 0x65, 0x31, 0x8b, 0x50, 0xef, 0x36, 0x8d, 0xb6, 0x77, 0x92, 0x3f, 0x2d, 0x9f, 0x7d, 0x9d,
0x8d, 0x36, 0x4a, 0x38, 0x46, 0x42, 0xd1, 0xbd, 0xdb, 0x24, 0xf8, 0xe3, 0x70, 0xd7, 0x12, 0x1a,
0xc2, 0x43, 0x2a, 0x04, 0x0b, 0xc8, 0xdc, 0xb5, 0x42, 0x9b, 0x89, 0x74, 0x8f, 0x7d, 0x79, 0x62,
0x6d, 0xe3, 0xc4, 0x11, 0xb6, 0x1b, 0x43, 0x93, 0xe0, 0x88, 0x6e, 0xf8, 0xd0, 0x5b, 0x28, 0x04,
0x1f, 0x3d, 0x56, 0x2f, 0x9d, 0x28, 0xa7, 0xd5, 0xb3, 0xaf, 0xb6, 0x8b, 0x20, 0xc6, 0x26, 0xc6,
0xf0, 0xa3, 0xc7, 0xb0, 0x24, 0x69, 0xe7, 0x00, 0xf7, 0x3e, 0x54, 0x86, 0x7d, 0x6c, 0xb4, 0x46,
0x1d, 0x1d, 0xab, 0x0f, 0x50, 0x09, 0x0a, 0xfa, 0xc0, 0x78, 0xaf, 0x2a, 0xa8, 0x0a, 0xa0, 0x9b,
0xa6, 0x31, 0x24, 0xfd, 0x5e, 0xe7, 0x83, 0x9a, 0xd3, 0xbe, 0x4d, 0x49, 0x52, 0x4e, 0x75, 0xd8,
0xbf, 0x63, 0xbe, 0x54, 0x41, 0x24, 0xa4, 0x03, 0x9c, 0x9a, 0xef, 0x0a, 0x25, 0x45, 0xcd, 0x69,
0x17, 0x50, 0x5e, 0x91, 0x01, 0xfa, 0x06, 0x8e, 0x42, 0x27, 0x95, 0x02, 0xb3, 0xc8, 0xcc, 0x76,
0xc7, 0x75, 0xe5, 0x24, 0x7f, 0x7a, 0x80, 0xd5, 0xd5, 0x85, 0x96, 0xed, 0x8e, 0xb5, 0x5f, 0xa0,
0x96, 0xb9, 0x7e, 0xf4, 0x05, 0x54, 0x52, 0xc9, 0x10, 0x6e, 0x09, 0x49, 0x2d, 0xe2, 0x72, 0xea,
0x6b, 0x5b, 0x02, 0xbd, 0x84, 0xea, 0x12, 0xe2, 0xd0, 0x39, 0x8b, 0x14, 0x1e, 0xc5, 0x3f, 0x4c,
0xbd, 0xbd, 0xc8, 0xa9, 0xfd, 0x9b, 0x83, 0xc3, 0x35, 0x8d, 0x23, 0x1d, 0x0e, 0x85, 0x67, 0xf3,
0x60, 0x79, 0x33, 0xf1, 0xc3, 0x7a, 0x96, 0xad, 0xa9, 0x29, 0x41, 0xc9, 0x9d, 0x54, 0xc4, 0x8a,
0x85, 0x38, 0x3c, 0xbd, 0xcf, 0x82, 0x38, 0x34, 0xe0, 0x77, 0x8c, 0xd8, 0x7c, 0xec, 0x53, 0x9f,
0xb3, 0xf4, 0xa9, 0x6d, 0x91, 0x53, 0x4a, 0xe9, 0x49, 0x46, 0x27, 0x25, 0x44, 0x72, 0xda, 0xb1,
0x14, 0xc9, 0x69, 0x65, 0x2b, 0x8b, 0x2d, 0xc8, 0x94, 0xdb, 0x4c, 0x24, 0x6f, 0x51, 0xdb, 0xbd,
0xc7, 0x15, 0x5b, 0x5c, 0x47, 0x48, 0x8c, 0xc2, 0x0d, 0x1f, 0xea, 0xc2, 0x91, 0x08, 0xa8, 0x63,
0x51, 0xdb, 0x75, 0x58, 0x5a, 0x87, 0xf8, 0x69, 0x9e, 0x6c, 0xd4, 0x61, 0x09, 0x4c, 0x6a, 0xa1,
0x8a, 0x8c, 0x47, 0xfb, 0x1e, 0x1e, 0xef, 0x4c, 0x2e, 0x92, 0x0e, 0x73, 0xe8, 0xd8, 0x66, 0x96,
0xac, 0x74, 0x09, 0xa7, 0xa6, 0xd6, 0x00, 0xb4, 0x79, 0xde, 0x4f, 0xe0, 0x7f, 0x82, 0xca, 0xea,
0xa5, 0xa0, 0x16, 0xd4, 0xe4, 0xb5, 0x10, 0x8b, 0xcf, 0x99, 0x23, 0xc5, 0xa9, 0xc8, 0x97, 0xfc,
0x7c, 0xeb, 0x5d, 0x5e, 0xa5, 0x28, 0x5c, 0x15, 0x6b, 0xb6, 0xf6, 0xb7, 0x02, 0x6a, 0x36, 0xcd,
0xff, 0x2d, 0x3a, 0x3a, 0x87, 0x47, 0x22, 0xf0, 0xb9, 0x47, 0xde, 0xbc, 0x26, 0x63, 0x1e, 0x64,
0x84, 0x52, 0xc2, 0x9f, 0xc9, 0xd5, 0x37, 0xaf, 0x2f, 0x79, 0xb0, 0xac, 0x9a, 0xf6, 0x4f, 0x0e,
0xaa, 0xeb, 0x71, 0xd1, 0x05, 0x14, 0xef, 0xa8, 0x1d, 0x32, 0x59, 0x96, 0xea, 0xd9, 0x97, 0x9f,
0x3e, 0x46, 0xe3, 0x26, 0xc2, 0xe2, 0x98, 0x82, 0x1e, 0xc1, 0x9e, 0xc3, 0x66, 0x34, 0x60, 0xc9,
0x9e, 0x89, 0x15, 0xb5, 0x68, 0x11, 0x4e, 0xa7, 0x7c, 0x41, 0xe4, 0x21, 0x3c, 0xee, 0xcc, 0x12,
0x69, 0x6d, 0xb4, 0x68, 0x53, 0xe2, 0xcc, 0x14, 0x86, 0x6b, 0x62, 0xdd, 0xa1, 0xfd, 0x09, 0x45,
0xb9, 0x27, 0x3a, 0x86, 0xa3, 0x51, 0xcf, 0x1c, 0x18, 0xcd, 0xf6, 0x75, 0xdb, 0xb8, 0x22, 0x37,
0x7a, 0x67, 0x64, 0xa8, 0x0f, 0xd0, 0x3e, 0xe4, 0xf5, 0xcb, 0xb6, 0xaa, 0x20, 0x04, 0x55, 0xb3,
0x89, 0x0d, 0xa3, 0x47, 0xae, 0x8c, 0x9e, 0xd9, 0x1e, 0x7e, 0x50, 0x73, 0xa8, 0x02, 0xa5, 0x8e,
0xde, 0x6b, 0x8d, 0xf4, 0x96, 0xa1, 0xe6, 0xd1, 0x73, 0x78, 0x32, 0x34, 0xde, 0x0f, 0x47, 0xd8,
0x20, 0xcd, 0x7e, 0x77, 0x80, 0x0d, 0xd3, 0x6c, 0xf7, 0x7b, 0xe4, 0xba, 0x8f, 0xbb, 0xfa, 0x50,
0x2d, 0x20, 0x15, 0x2a, 0x2d, 0xac, 0x0f, 0x7e, 0x6c, 0x37, 0x4d, 0xa2, 0x0f, 0xda, 0x6a, 0x51,
0xc3, 0x50, 0xcb, 0x1c, 0x70, 0xb7, 0x90, 0xa2, 0xde, 0x61, 0xb1, 0x29, 0x0d, 0xed, 0x80, 0xc4,
0x49, 0x24, 0x4d, 0xed, 0x30, 0xf1, 0xc6, 0x91, 0x34, 0x1b, 0xe0, 0x7e, 0xa0, 0xa0, 0x5f, 0xa1,
0x2e, 0x27, 0xd0, 0xb6, 0x01, 0x12, 0x0b, 0xe3, 0xe5, 0xb6, 0x71, 0xb4, 0x39, 0x3c, 0x8e, 0xe9,
0x36, 0xb7, 0xd6, 0x83, 0xe3, 0xad, 0xf8, 0xa8, 0x19, 0x7a, 0x74, 0x72, 0x4b, 0x67, 0x71, 0xa3,
0x93, 0xc9, 0x1c, 0xe0, 0x72, 0xe2, 0x8b, 0xda, 0x1c, 0x42, 0x50, 0xf0, 0x68, 0xf0, 0x5b, 0x92,
0x86, 0xfc, 0xd6, 0xbe, 0x8b, 0x1e, 0xe5, 0xae, 0x29, 0x95, 0x12, 0x94, 0x15, 0x02, 0x05, 0xb4,
0x39, 0x8d, 0xd0, 0x8b, 0x68, 0xf0, 0x7a, 0x24, 0xed, 0xfe, 0x51, 0xa6, 0xf9, 0x68, 0xb8, 0x7a,
0x37, 0xb1, 0x07, 0xbd, 0x82, 0xa3, 0x78, 0xe0, 0x25, 0x10, 0x12, 0xd0, 0x59, 0x72, 0x90, 0x9a,
0x5c, 0x48, 0x80, 0x43, 0x3a, 0xbb, 0x7c, 0x05, 0x68, 0xe2, 0xce, 0x33, 0x65, 0xfa, 0xf9, 0x61,
0x62, 0x93, 0xd8, 0x26, 0xf2, 0xef, 0xd1, 0x78, 0x4f, 0xfe, 0x9c, 0xff, 0x17, 0x00, 0x00, 0xff,
0xff, 0x6b, 0x05, 0xbf, 0x99, 0x35, 0x09, 0x00, 0x00,
}

View File

@ -0,0 +1,162 @@
// Messages describing APK Set's table of contents (toc.pb entry).
// Please be advised that the ultimate source is at
// https://github.com/google/bundletool/tree/master/src/main/proto
// so you have been warned.
syntax = "proto3";
package android.bundle;
option go_package = "android_bundle_proto";
option java_package = "com.android.bundle";
message BundleConfig {
Bundletool bundletool = 1;
Optimizations optimizations = 2;
Compression compression = 3;
// Resources to be always kept in the master split.
MasterResources master_resources = 4;
ApexConfig apex_config = 5;
// APKs to be signed with the same key as generated APKs.
repeated UnsignedEmbeddedApkConfig unsigned_embedded_apk_config = 6;
AssetModulesConfig asset_modules_config = 7;
enum BundleType {
REGULAR = 0;
APEX = 1;
ASSET_ONLY = 2;
}
BundleType type = 8;
}
message Bundletool {
reserved 1;
// Version of BundleTool used to build the Bundle.
string version = 2;
}
message Compression {
// Glob matching the list of files to leave uncompressed in the APKs.
// The matching is done against the path of files in the APK, thus excluding
// the name of the modules, and using forward slash ("/") as a name separator.
// Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
repeated string uncompressed_glob = 1;
}
// Resources to keep in the master split.
message MasterResources {
// Resource IDs to be kept in master split.
repeated int32 resource_ids = 1;
// Resource names to be kept in master split.
repeated string resource_names = 2;
}
message Optimizations {
SplitsConfig splits_config = 1;
// This is for uncompressing native libraries on M+ devices (L+ devices on
// instant apps).
UncompressNativeLibraries uncompress_native_libraries = 2;
// This is for uncompressing dex files on P+ devices.
UncompressDexFiles uncompress_dex_files = 3;
// Configuration for the generation of standalone APKs.
// If no StandaloneConfig is set, the configuration is inherited from
// splits_config.
StandaloneConfig standalone_config = 4;
}
message UncompressNativeLibraries {
bool enabled = 1;
}
message UncompressDexFiles {
bool enabled = 1;
}
// Optimization configuration used to generate Split APKs.
message SplitsConfig {
repeated SplitDimension split_dimension = 1;
}
// Optimization configuration used to generate Standalone APKs.
message StandaloneConfig {
// Device targeting dimensions to shard.
repeated SplitDimension split_dimension = 1;
// Whether 64 bit libraries should be stripped from Standalone APKs.
bool strip_64_bit_libraries = 2;
}
message SplitDimension {
enum Value {
UNSPECIFIED_VALUE = 0;
ABI = 1;
SCREEN_DENSITY = 2;
LANGUAGE = 3;
TEXTURE_COMPRESSION_FORMAT = 4;
// BEGIN-INTERNAL
GRAPHICS_API = 5;
// END-INTERNAL
}
Value value = 1;
// If set to 'true', indicates that APKs should *not* be split by this
// dimension.
bool negate = 2;
// Optional transformation to be applied to asset directories where
// the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
SuffixStripping suffix_stripping = 3;
}
message SuffixStripping {
// If set to 'true', indicates that the targeting suffix should be removed
// from assets paths for this dimension when splits (or asset slices) are
// generated.
// This only applies to assets.
// For example a folder with path "assets/level1_textures#tcf_etc1"
// would be outputted to "assets/level1_textures". File contents are
// unchanged.
bool enabled = 1;
// The default suffix to be used for the cases where separate slices can't
// be generated for this dimension. In the case of standalone/universal APKs
// generation, stripping the suffix can lead to file name collisions. This
// default suffix defines the directories to retain. The others are
// discarded: standalone/universal APKs will contain only directories
// targeted at this value for the dimension.
//
// If not set or empty, the fallback directory in each directory group will be
// used (for example, if both "assets/level1_textures#tcf_etc1" and
// "assets/level1_textures" are present and the default suffix is empty,
// then only "assets/level1_textures" will be used).
string default_suffix = 2;
}
// Configuration for processing APEX bundles.
// https://source.android.com/devices/tech/ota/apex
message ApexConfig {
// Configuration for processing of APKs embedded in an APEX image.
repeated ApexEmbeddedApkConfig apex_embedded_apk_config = 1;
}
message ApexEmbeddedApkConfig {
// Android package name of the APK.
string package_name = 1;
// Path to the APK within the APEX system image.
string path = 2;
}
message UnsignedEmbeddedApkConfig {
// Path to the APK inside the module (e.g. if the path inside the bundle
// is split/assets/example.apk, this will be assets/example.apk).
string path = 1;
}
message AssetModulesConfig {
// App versionCodes that will be updated with these asset modules.
// Only relevant for asset-only bundles.
repeated int64 app_version = 1;
// Version tag for the asset upload.
// Only relevant for asset-only bundles.
string asset_version_tag = 2;
}

View File

@ -0,0 +1,12 @@
#!/bin/bash
# Generates the golang source file of protos file describing APK set table of
# contents (toc.pb file).
set -e
function die() { echo "ERROR: $1" >&2; exit 1; }
readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
hash aprotoc &>/dev/null || die "could not find aprotoc. ${error_msg}"
# TODO(asmundak): maybe have the paths relative to repo top?
(cd "${0%/*}" && aprotoc --go_out=paths=source_relative:. commands.proto config.proto targeting.proto ) || die "build failed. ${error_msg}"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,232 @@
// Messages describing APK Set's table of contents (toc.pb entry).
// Please be advised that the ultimate source is at
// https://github.com/google/bundletool/tree/master/src/main/proto
// so you have been warned.
syntax = "proto3";
package android.bundle;
option go_package = "android_bundle_proto";
option java_package = "com.android.bundle";
// Targeting on the level of variants.
message VariantTargeting {
SdkVersionTargeting sdk_version_targeting = 1;
AbiTargeting abi_targeting = 2;
ScreenDensityTargeting screen_density_targeting = 3;
MultiAbiTargeting multi_abi_targeting = 4;
TextureCompressionFormatTargeting texture_compression_format_targeting = 5;
}
// Targeting on the level of individual APKs.
message ApkTargeting {
AbiTargeting abi_targeting = 1;
GraphicsApiTargeting graphics_api_targeting = 2;
LanguageTargeting language_targeting = 3;
ScreenDensityTargeting screen_density_targeting = 4;
SdkVersionTargeting sdk_version_targeting = 5;
TextureCompressionFormatTargeting texture_compression_format_targeting = 6;
MultiAbiTargeting multi_abi_targeting = 7;
SanitizerTargeting sanitizer_targeting = 8;
}
// Targeting on the module level.
// The semantic of the targeting is the "AND" rule on all immediate values.
message ModuleTargeting {
SdkVersionTargeting sdk_version_targeting = 1;
repeated DeviceFeatureTargeting device_feature_targeting = 2;
UserCountriesTargeting user_countries_targeting = 3;
}
// User Countries targeting describing an inclusive/exclusive list of country
// codes that module targets.
message UserCountriesTargeting {
// List of country codes in the two-letter CLDR territory format.
repeated string country_codes = 1;
// Indicates if the list above is exclusive.
bool exclude = 2;
}
message ScreenDensity {
enum DensityAlias {
DENSITY_UNSPECIFIED = 0;
NODPI = 1;
LDPI = 2;
MDPI = 3;
TVDPI = 4;
HDPI = 5;
XHDPI = 6;
XXHDPI = 7;
XXXHDPI = 8;
}
oneof density_oneof {
DensityAlias density_alias = 1;
int32 density_dpi = 2;
}
}
// Wrapper message for `int32`.
//
// The JSON representation for `Int32Value` is JSON number.
message Int32Value {
// The int32 value.
int32 value = 1;
}
message SdkVersion {
// Inclusive.
Int32Value min = 1;
}
message GraphicsApi {
oneof api_oneof {
// Inclusive.
OpenGlVersion min_open_gl_version = 1;
// Inclusive.
VulkanVersion min_vulkan_version = 2;
}
}
message VulkanVersion {
int32 major = 1; // VK_VERSION_MAJOR
int32 minor = 2; // VK_VERSION_MINOR
}
message OpenGlVersion {
// e.g. OpenGL ES 3.2 is represented as { major: 3, minor: 2 }
int32 major = 1; // GL_MAJOR_VERSION
int32 minor = 2; // GL_MINOR_VERSION
}
message TextureCompressionFormat {
enum TextureCompressionFormatAlias {
UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT = 0;
ETC1_RGB8 = 1;
PALETTED = 2;
THREE_DC = 3;
ATC = 4;
LATC = 5;
DXT1 = 6;
S3TC = 7;
PVRTC = 8;
ASTC = 9;
ETC2 = 10;
}
TextureCompressionFormatAlias alias = 1;
}
message Abi {
enum AbiAlias {
UNSPECIFIED_CPU_ARCHITECTURE = 0;
ARMEABI = 1;
ARMEABI_V7A = 2;
ARM64_V8A = 3;
X86 = 4;
X86_64 = 5;
MIPS = 6;
MIPS64 = 7;
}
AbiAlias alias = 1;
}
message MultiAbi {
repeated Abi abi = 1;
}
message Sanitizer {
enum SanitizerAlias {
NONE = 0;
HWADDRESS = 1;
}
SanitizerAlias alias = 1;
}
message DeviceFeature {
string feature_name = 1;
// Equivalent of android:glEsVersion or android:version in <uses-feature>.
int32 feature_version = 2;
}
// Targeting specific for directories under assets/.
message AssetsDirectoryTargeting {
AbiTargeting abi = 1;
GraphicsApiTargeting graphics_api = 2;
TextureCompressionFormatTargeting texture_compression_format = 3;
LanguageTargeting language = 4;
}
// Targeting specific for directories under lib/.
message NativeDirectoryTargeting {
Abi abi = 1;
GraphicsApi graphics_api = 2;
TextureCompressionFormat texture_compression_format = 3;
Sanitizer sanitizer = 4;
}
// Targeting specific for image files under apex/.
message ApexImageTargeting {
MultiAbiTargeting multi_abi = 1;
}
message AbiTargeting {
repeated Abi value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated Abi alternatives = 2;
}
message MultiAbiTargeting {
repeated MultiAbi value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated MultiAbi alternatives = 2;
}
message ScreenDensityTargeting {
repeated ScreenDensity value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated ScreenDensity alternatives = 2;
}
message LanguageTargeting {
// ISO-639: 2 or 3 letter language code.
repeated string value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated string alternatives = 2;
}
message GraphicsApiTargeting {
repeated GraphicsApi value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated GraphicsApi alternatives = 2;
}
message SdkVersionTargeting {
repeated SdkVersion value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated SdkVersion alternatives = 2;
}
message TextureCompressionFormatTargeting {
repeated TextureCompressionFormat value = 1;
// Targeting of other sibling directories that were in the Bundle.
// For master splits this is targeting of other master splits.
repeated TextureCompressionFormat alternatives = 2;
}
message SanitizerTargeting {
repeated Sanitizer value = 1;
}
// Since other atom targeting messages have the "OR" semantic on values
// the DeviceFeatureTargeting represents only one device feature to retain
// that convention.
message DeviceFeatureTargeting {
DeviceFeature required_feature = 1;
}

467
cmd/extract_apks/main.go Normal file
View File

@ -0,0 +1,467 @@
// 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.
// Copies all the entries (APKs/APEXes) matching the target configuration from the given
// APK set into a zip file. Run it without arguments to see usage details.
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"regexp"
"strings"
"github.com/golang/protobuf/proto"
"android/soong/cmd/extract_apks/bundle_proto"
"android/soong/third_party/zip"
)
type TargetConfig struct {
sdkVersion int32
screenDpi map[android_bundle_proto.ScreenDensity_DensityAlias]bool
abis map[android_bundle_proto.Abi_AbiAlias]bool
allowPrereleased bool
stem string
}
// An APK set is a zip archive. An entry 'toc.pb' describes its contents.
// It is a protobuf message BuildApkResult.
type Toc *android_bundle_proto.BuildApksResult
type ApkSet struct {
path string
reader *zip.ReadCloser
entries map[string]*zip.File
}
func newApkSet(path string) (*ApkSet, error) {
apkSet := &ApkSet{path: path, entries: make(map[string]*zip.File)}
var err error
if apkSet.reader, err = zip.OpenReader(apkSet.path); err != nil {
return nil, err
}
for _, f := range apkSet.reader.File {
apkSet.entries[f.Name] = f
}
return apkSet, nil
}
func (apkSet *ApkSet) getToc() (Toc, error) {
var err error
tocFile, ok := apkSet.entries["toc.pb"]
if !ok {
return nil, fmt.Errorf("%s: APK set should have toc.pb entry", apkSet.path)
}
rc, err := tocFile.Open()
if err != nil {
return nil, err
}
bytes := make([]byte, tocFile.FileHeader.UncompressedSize64)
if _, err := rc.Read(bytes); err != io.EOF {
return nil, err
}
rc.Close()
buildApksResult := new(android_bundle_proto.BuildApksResult)
if err = proto.Unmarshal(bytes, buildApksResult); err != nil {
return nil, err
}
return buildApksResult, nil
}
func (apkSet *ApkSet) close() {
apkSet.reader.Close()
}
// Matchers for selection criteria
type abiTargetingMatcher struct {
*android_bundle_proto.AbiTargeting
}
func (m abiTargetingMatcher) matches(config TargetConfig) bool {
if m.AbiTargeting == nil {
return true
}
if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
return true
}
for _, v := range m.GetValue() {
if _, ok := config.abis[v.Alias]; ok {
return true
}
}
return false
}
type apkDescriptionMatcher struct {
*android_bundle_proto.ApkDescription
}
func (m apkDescriptionMatcher) matches(config TargetConfig) bool {
return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config)
}
type apkTargetingMatcher struct {
*android_bundle_proto.ApkTargeting
}
func (m apkTargetingMatcher) matches(config TargetConfig) bool {
return m.ApkTargeting == nil ||
(abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
languageTargetingMatcher{m.LanguageTargeting}.matches(config) &&
screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config))
}
type languageTargetingMatcher struct {
*android_bundle_proto.LanguageTargeting
}
func (m languageTargetingMatcher) matches(_ TargetConfig) bool {
if m.LanguageTargeting == nil {
return true
}
log.Fatal("language based entry selection is not implemented")
return false
}
type moduleMetadataMatcher struct {
*android_bundle_proto.ModuleMetadata
}
func (m moduleMetadataMatcher) matches(config TargetConfig) bool {
return m.ModuleMetadata == nil ||
(m.GetDeliveryType() == android_bundle_proto.DeliveryType_INSTALL_TIME &&
moduleTargetingMatcher{m.Targeting}.matches(config) &&
!m.IsInstant)
}
type moduleTargetingMatcher struct {
*android_bundle_proto.ModuleTargeting
}
func (m moduleTargetingMatcher) matches(config TargetConfig) bool {
return m.ModuleTargeting == nil ||
(sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
userCountriesTargetingMatcher{m.UserCountriesTargeting}.matches(config))
}
type multiAbiTargetingMatcher struct {
*android_bundle_proto.MultiAbiTargeting
}
func (t multiAbiTargetingMatcher) matches(_ TargetConfig) bool {
if t.MultiAbiTargeting == nil {
return true
}
log.Fatal("multiABI based selection is not implemented")
return false
}
type screenDensityTargetingMatcher struct {
*android_bundle_proto.ScreenDensityTargeting
}
func (m screenDensityTargetingMatcher) matches(config TargetConfig) bool {
if m.ScreenDensityTargeting == nil {
return true
}
if _, ok := config.screenDpi[android_bundle_proto.ScreenDensity_DENSITY_UNSPECIFIED]; ok {
return true
}
for _, v := range m.GetValue() {
switch x := v.GetDensityOneof().(type) {
case *android_bundle_proto.ScreenDensity_DensityAlias_:
if _, ok := config.screenDpi[x.DensityAlias]; ok {
return true
}
default:
log.Fatal("For screen density, only DPI name based entry selection (e.g. HDPI, XHDPI) is implemented")
}
}
return false
}
type sdkVersionTargetingMatcher struct {
*android_bundle_proto.SdkVersionTargeting
}
func (m sdkVersionTargetingMatcher) matches(config TargetConfig) bool {
const preReleaseVersion = 10000
if m.SdkVersionTargeting == nil {
return true
}
if len(m.Value) > 1 {
log.Fatal(fmt.Sprintf("sdk_version_targeting should not have multiple values:%#v", m.Value))
}
// Inspect only sdkVersionTargeting.Value.
// Even though one of the SdkVersionTargeting.Alternatives values may be
// better matching, we will select all of them
return m.Value[0].Min == nil ||
m.Value[0].Min.Value <= config.sdkVersion ||
(config.allowPrereleased && m.Value[0].Min.Value == preReleaseVersion)
}
type textureCompressionFormatTargetingMatcher struct {
*android_bundle_proto.TextureCompressionFormatTargeting
}
func (m textureCompressionFormatTargetingMatcher) matches(_ TargetConfig) bool {
if m.TextureCompressionFormatTargeting == nil {
return true
}
log.Fatal("texture based entry selection is not implemented")
return false
}
type userCountriesTargetingMatcher struct {
*android_bundle_proto.UserCountriesTargeting
}
func (m userCountriesTargetingMatcher) matches(_ TargetConfig) bool {
if m.UserCountriesTargeting == nil {
return true
}
log.Fatal("country based entry selection is not implemented")
return false
}
type variantTargetingMatcher struct {
*android_bundle_proto.VariantTargeting
}
func (m variantTargetingMatcher) matches(config TargetConfig) bool {
if m.VariantTargeting == nil {
return true
}
return sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config) &&
screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
textureCompressionFormatTargetingMatcher{m.TextureCompressionFormatTargeting}.matches(config)
}
type SelectionResult struct {
moduleName string
entries []string
}
// Return all entries matching target configuration
func selectApks(toc Toc, targetConfig TargetConfig) SelectionResult {
var result SelectionResult
for _, variant := range (*toc).GetVariant() {
if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig)) {
continue
}
for _, as := range variant.GetApkSet() {
if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
continue
}
for _, apkdesc := range as.GetApkDescription() {
if (apkDescriptionMatcher{apkdesc}).matches(targetConfig) {
result.entries = append(result.entries, apkdesc.GetPath())
// TODO(asmundak): As it turns out, moduleName which we get from
// the ModuleMetadata matches the module names of the generated
// entry paths just by coincidence, only for the split APKs. We
// need to discuss this with bundletool folks.
result.moduleName = as.GetModuleMetadata().GetName()
}
}
// we allow only a single module, so bail out here if we found one
if result.moduleName != "" {
return result
}
}
}
return result
}
type Zip2ZipWriter interface {
CopyFrom(file *zip.File, name string) error
}
// Writes out selected entries, renaming them as needed
func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
writer Zip2ZipWriter) error {
// Renaming rules:
// splits/MODULE-master.apk to STEM.apk
// else
// splits/MODULE-*.apk to STEM>-$1.apk
// TODO(asmundak):
// add more rules, for .apex files
renameRules := []struct {
rex *regexp.Regexp
repl string
}{
{
regexp.MustCompile(`^.*/` + selected.moduleName + `-master\.apk$`),
config.stem + `.apk`,
},
{
regexp.MustCompile(`^.*/` + selected.moduleName + `(-.*\.apk)$`),
config.stem + `$1`,
},
}
renamer := func(path string) (string, bool) {
for _, rr := range renameRules {
if rr.rex.MatchString(path) {
return rr.rex.ReplaceAllString(path, rr.repl), true
}
}
return "", false
}
entryOrigin := make(map[string]string) // output entry to input entry
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)
}
inName := apkFile.Name
outName, ok := renamer(inName)
if !ok {
log.Fatalf("selected an entry with unexpected name %s", inName)
}
if origin, ok := entryOrigin[inName]; ok {
log.Fatalf("selected entries %s and %s will have the same output name %s",
origin, inName, outName)
}
entryOrigin[outName] = inName
if err := writer.CopyFrom(apkFile, outName); err != nil {
return err
}
}
return nil
}
// Arguments parsing
var (
outputZip = flag.String("o", "", "output zip containing extracted entries")
targetConfig = TargetConfig{
screenDpi: map[android_bundle_proto.ScreenDensity_DensityAlias]bool{},
abis: map[android_bundle_proto.Abi_AbiAlias]bool{},
}
)
// Parse abi values
type abiFlagValue struct {
targetConfig *TargetConfig
}
func (a abiFlagValue) String() string {
return "all"
}
func (a abiFlagValue) Set(abiList string) error {
if abiList == "none" {
return nil
}
if abiList == "all" {
targetConfig.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE] = true
return nil
}
for _, abi := range strings.Split(abiList, ",") {
v, ok := android_bundle_proto.Abi_AbiAlias_value[abi]
if !ok {
return fmt.Errorf("bad ABI value: %q", abi)
}
targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = true
}
return nil
}
// Parse screen density values
type screenDensityFlagValue struct {
targetConfig *TargetConfig
}
func (s screenDensityFlagValue) String() string {
return "none"
}
func (s screenDensityFlagValue) Set(densityList string) error {
if densityList == "none" {
return nil
}
if densityList == "all" {
targetConfig.screenDpi[android_bundle_proto.ScreenDensity_DENSITY_UNSPECIFIED] = true
return nil
}
for _, density := range strings.Split(densityList, ",") {
v, found := android_bundle_proto.ScreenDensity_DensityAlias_value[density]
if !found {
return fmt.Errorf("bad screen density value: %q", density)
}
targetConfig.screenDpi[android_bundle_proto.ScreenDensity_DensityAlias(v)] = true
}
return nil
}
func processArgs() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-zip> -sdk-version value -abis value -screen-densities value <APK set>`)
flag.PrintDefaults()
os.Exit(2)
}
version := flag.Uint("sdk-version", 0, "SDK version")
flag.Var(abiFlagValue{&targetConfig}, "abis",
"'all' or comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64")
flag.Var(screenDensityFlagValue{&targetConfig}, "screen-densities",
"'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)")
flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false,
"allow prereleased")
flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name")
flag.Parse()
if (*outputZip == "") || len(flag.Args()) != 1 || *version == 0 || targetConfig.stem == "" {
flag.Usage()
}
targetConfig.sdkVersion = int32(*version)
}
func main() {
processArgs()
var toc Toc
apkSet, err := newApkSet(flag.Arg(0))
if err == nil {
defer apkSet.close()
toc, err = apkSet.getToc()
}
if err != nil {
log.Fatal(err)
}
sel := selectApks(toc, targetConfig)
if len(sel.entries) == 0 {
log.Fatalf("there are no entries for the target configuration: %#v", targetConfig)
}
outFile, err := os.Create(*outputZip)
if err != nil {
log.Fatal(err)
}
defer outFile.Close()
writer := zip.NewWriter(outFile)
defer func() {
if err := writer.Close(); err != nil {
log.Fatal(err)
}
}()
if err = apkSet.writeApks(sel, targetConfig, writer); err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,246 @@
// 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 main
import (
"fmt"
"github.com/golang/protobuf/proto"
"reflect"
"testing"
bp "android/soong/cmd/extract_apks/bundle_proto"
"android/soong/third_party/zip"
)
type TestConfigDesc struct {
name string
targetConfig TargetConfig
expected SelectionResult
}
type TestDesc struct {
protoText string
configs []TestConfigDesc
}
var (
testCases = []TestDesc{
{
protoText: `
variant {
targeting {
sdk_version_targeting {
value { min { value: 29 } } } }
apk_set {
module_metadata {
name: "base" targeting {} delivery_type: INSTALL_TIME }
apk_description {
targeting {
screen_density_targeting {
value { density_alias: LDPI } }
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-ldpi.apk"
split_apk_metadata { split_id: "config.ldpi" } }
apk_description {
targeting {
screen_density_targeting {
value { density_alias: MDPI } }
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-mdpi.apk"
split_apk_metadata { split_id: "config.mdpi" } }
apk_description {
targeting {
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-master.apk"
split_apk_metadata { is_master_split: true } }
apk_description {
targeting {
abi_targeting {
value { alias: ARMEABI_V7A } }
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-armeabi_v7a.apk"
split_apk_metadata { split_id: "config.armeabi_v7a" } }
apk_description {
targeting {
abi_targeting {
value { alias: ARM64_V8A } }
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-arm64_v8a.apk"
split_apk_metadata { split_id: "config.arm64_v8a" } }
apk_description {
targeting {
abi_targeting {
value { alias: X86 } }
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-x86.apk"
split_apk_metadata { split_id: "config.x86" } }
apk_description {
targeting {
abi_targeting {
value { alias: X86_64 } }
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-x86_64.apk"
split_apk_metadata { split_id: "config.x86_64" } } }
}
bundletool {
version: "0.10.3" }
`,
configs: []TestConfigDesc{
{
name: "one",
targetConfig: TargetConfig{
sdkVersion: 29,
screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
},
abis: map[bp.Abi_AbiAlias]bool{
bp.Abi_ARMEABI_V7A: true,
bp.Abi_ARM64_V8A: true,
},
},
expected: SelectionResult{
"base",
[]string{
"splits/base-ldpi.apk",
"splits/base-mdpi.apk",
"splits/base-master.apk",
"splits/base-armeabi_v7a.apk",
"splits/base-arm64_v8a.apk",
},
},
},
{
name: "two",
targetConfig: TargetConfig{
sdkVersion: 29,
screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
bp.ScreenDensity_LDPI: true,
},
abis: map[bp.Abi_AbiAlias]bool{},
},
expected: SelectionResult{
"base",
[]string{
"splits/base-ldpi.apk",
"splits/base-master.apk",
},
},
},
{
name: "three",
targetConfig: TargetConfig{
sdkVersion: 20,
screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
bp.ScreenDensity_LDPI: true,
},
abis: map[bp.Abi_AbiAlias]bool{},
},
expected: SelectionResult{
"",
nil,
},
},
},
},
{
protoText: `
variant {
targeting {
sdk_version_targeting {
value { min { value: 10000 } } } }
apk_set {
module_metadata {
name: "base" targeting {} delivery_type: INSTALL_TIME }
apk_description {
targeting {
sdk_version_targeting {
value { min { value: 21 } } } }
path: "splits/base-master.apk"
split_apk_metadata { is_master_split: true } } } }`,
configs: []TestConfigDesc{
{
name: "Prerelease",
targetConfig: TargetConfig{
sdkVersion: 30,
screenDpi: map[bp.ScreenDensity_DensityAlias]bool{},
abis: map[bp.Abi_AbiAlias]bool{},
allowPrereleased: true,
},
expected: SelectionResult{
"base",
[]string{"splits/base-master.apk"},
},
},
},
},
}
)
func TestSelectApks(t *testing.T) {
for _, testCase := range testCases {
var toc bp.BuildApksResult
if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
t.Fatal(err)
}
for _, config := range testCase.configs {
actual := selectApks(&toc, config.targetConfig)
if !reflect.DeepEqual(config.expected, actual) {
t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual)
}
}
}
}
type testZip2ZipWriter struct {
entries map[string]string
}
func (w testZip2ZipWriter) CopyFrom(file *zip.File, out string) error {
if x, ok := w.entries[out]; ok {
return fmt.Errorf("%s and %s both write to %s", x, file.Name, out)
}
w.entries[out] = file.Name
return nil
}
func TestWriteZip(t *testing.T) {
// what we write from what
expected := map[string]string{
"Foo.apk": "splits/mybase-master.apk",
"Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
}
apkSet := ApkSet{entries: make(map[string]*zip.File)}
sel := SelectionResult{moduleName: "mybase"}
for _, in := range expected {
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: "Foo"}
if err := apkSet.writeApks(sel, config, writer); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(expected, writer.entries) {
t.Errorf("expected %v, got %v", expected, writer.entries)
}
}