Class: AppInfo::AAB

Inherits:
Object show all
Extended by:
Forwardable
Includes:
Helper::HumanFileSize
Defined in:
lib/app_info/aab.rb

Overview

Parse APK file

Defined Under Namespace

Modules: Device

Constant Summary collapse

BASE_PATH =
'base'
BASE_MANIFEST =
"#{BASE_PATH}/manifest/AndroidManifest.xml"
BASE_RESOURCES =
"#{BASE_PATH}/resources.pb"

Constants included from Helper::HumanFileSize

Helper::HumanFileSize::FILE_SIZE_UNITS

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Helper::HumanFileSize

#file_to_human_size, #number_to_human_size

Constructor Details

#initialize(file) ⇒ AAB

Returns a new instance of AAB.



28
29
30
# File 'lib/app_info/aab.rb', line 28

def initialize(file)
  @file = file
end

Instance Attribute Details

#fileObject (readonly)

Returns the value of attribute file.



13
14
15
# File 'lib/app_info/aab.rb', line 13

def file
  @file
end

Instance Method Details

#activitiesObject



110
111
112
# File 'lib/app_info/aab.rb', line 110

def activities
  @activities ||= manifest.activities
end

#automotive?Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/app_info/aab.rb', line 89

def automotive?
  use_features.include?('android.hardware.type.automotive')
end

#certificatesObject



148
149
150
151
152
# File 'lib/app_info/aab.rb', line 148

def certificates
  @certificates ||= signs.each_with_object([]) do |sign, obj|
    obj << APK::Certificate.new(sign.path, sign.sign.certificates[0])
  end
end

#clear!Object



209
210
211
212
213
214
215
216
217
218
219
# File 'lib/app_info/aab.rb', line 209

def clear!
  return unless @contents

  FileUtils.rm_rf(@contents)

  @aab = nil
  @contents = nil
  @icons = nil
  @app_path = nil
  @info = nil
end

#componentsObject



118
119
120
# File 'lib/app_info/aab.rb', line 118

def components
  @components ||= manifest.components.transform_values
end

#contentsObject



221
222
223
# File 'lib/app_info/aab.rb', line 221

def contents
  @contents ||= File.join(Dir.mktmpdir, "AppInfo-android-#{SecureRandom.hex}")
end

#device_typeObject



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/app_info/aab.rb', line 60

def device_type
  if wear?
    Device::WATCH
  elsif tv?
    Device::TV
  elsif automotive?
    Device::AUTOMOTIVE
  else
    Device::PHONE
  end
end

#each_fileObject



154
155
156
157
158
159
160
# File 'lib/app_info/aab.rb', line 154

def each_file
  zip.each do |entry|
    next unless entry.file?

    yield entry.name, @zip.read(entry)
  end
end

#entry(name, base_path: BASE_PATH) ⇒ Object

Raises:



169
170
171
172
173
174
# File 'lib/app_info/aab.rb', line 169

def entry(name, base_path: BASE_PATH)
  entry = @zip.find_entry(File.join(base_path, name))
  raise NotFoundError, "'#{name}'" if entry.nil?

  entry
end

#iconsObject



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/app_info/aab.rb', line 190

def icons
  @icons ||= manifest.icons.each_with_object([]) do |res, obj|
    path = res.value
    filename = File.basename(path)
    filepath = File.join(contents, File.dirname(path))
    file = File.join(filepath, filename)
    FileUtils.mkdir_p filepath

    binary_data = read_file(path)
    File.write(file, binary_data, encoding: Encoding::BINARY)

    obj << {
      name: filename,
      file: file,
      dimensions: ImageSize.path(file).size
    }
  end
end

#manifestObject



176
177
178
179
# File 'lib/app_info/aab.rb', line 176

def manifest
  io = zip.read(zip.find_entry(BASE_MANIFEST))
  @manifest ||= Protobuf::Manifest.parse(io, resource)
end

#min_sdk_versionObject Also known as: min_os_version



93
94
95
# File 'lib/app_info/aab.rb', line 93

def min_sdk_version
  manifest.uses_sdk.min_sdk_version
end

#nameObject



56
57
58
# File 'lib/app_info/aab.rb', line 56

def name
  manifest.label
end

#osObject Also known as: file_type



36
37
38
# File 'lib/app_info/aab.rb', line 36

def os
  Platform::ANDROID
end

#package_nameObject Also known as: identifier, bundle_id



45
46
47
# File 'lib/app_info/aab.rb', line 45

def package_name
  manifest.package
end

#read_file(name, base_path: BASE_PATH) ⇒ Object



162
163
164
165
166
167
# File 'lib/app_info/aab.rb', line 162

def read_file(name, base_path: BASE_PATH)
  content = @zip.read(entry(name, base_path: base_path))
  return parse_binary_xml(content) if xml_file?(name)

  content
end

#resourceObject



181
182
183
184
# File 'lib/app_info/aab.rb', line 181

def resource
  io = zip.read(zip.find_entry(BASE_RESOURCES))
  @resource ||= Protobuf::Resources.parse(io)
end

#servicesObject



114
115
116
# File 'lib/app_info/aab.rb', line 114

def services
  @services ||= manifest.services
end

#sign_versionObject



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/app_info/aab.rb', line 122

def sign_version
  return 'v1' unless signs.empty?

  # when ?
  # https://source.android.com/security/apksigning/v2?hl=zh-cn
  #   'v2'
  # when ?
  # https://source.android.com/security/apksigning/v3?hl=zh-cn
  #   'v3'
  'unknown'
end

#signsObject



134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/app_info/aab.rb', line 134

def signs
  return @signs if @signs

  @signs = []
  each_file do |path, data|
    # find META-INF/xxx.{RSA|DSA}
    next unless path =~ %r{^META-INF/} && data.unpack('CC') == [0x30, 0x82]

    @signs << APK::Sign.new(path, OpenSSL::PKCS7.new(data))
  end

  @signs
end

#size(human_size: false) ⇒ Object



32
33
34
# File 'lib/app_info/aab.rb', line 32

def size(human_size: false)
  file_to_human_size(@file, human_size: human_size)
end

#target_sdk_versionObject



98
99
100
# File 'lib/app_info/aab.rb', line 98

def target_sdk_version
  manifest.uses_sdk.target_sdk_version
end

#tv?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/app_info/aab.rb', line 85

def tv?
  use_features.include?('android.software.leanback')
end

#use_featuresObject



102
103
104
# File 'lib/app_info/aab.rb', line 102

def use_features
  @use_features ||= manifest&.uses_feature&.map(&:name)
end

#use_permissionsObject



106
107
108
# File 'lib/app_info/aab.rb', line 106

def use_permissions
  @use_permissions ||= manifest&.uses_permission&.map(&:name)
end

#version_codeObject Also known as: build_version



51
52
53
# File 'lib/app_info/aab.rb', line 51

def version_code
  manifest.version_code.to_s
end

#wear?Boolean

TODO: find a way to detect Found answer but not works: stackoverflow.com/questions/9279111/determine-if-the-device-is-a-smartphone-or-tablet def tablet?

resource.first_package
        .entries('bool')
        .select{|e| e.name == 'isTablet' }
        .size >= 1

end

Returns:

  • (Boolean)


81
82
83
# File 'lib/app_info/aab.rb', line 81

def wear?
  use_features.include?('android.hardware.type.watch')
end

#zipObject



186
187
188
# File 'lib/app_info/aab.rb', line 186

def zip
  @zip ||= Zip::File.open(@file)
end