ruby_android_apk 0.7.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,151 @@
1
+ require_relative 'dex_object'
2
+
3
+ module Android
4
+ class Dex
5
+ # class information in dex
6
+ # @!attribute [r] name
7
+ # @return [String] class name
8
+ # @!attribute [r] super_class
9
+ # @return [String] super class name
10
+ class ClassInfo
11
+ # no index flag
12
+ NO_INDEX = 0xffffffff
13
+
14
+ # @return [ClassAccessFlag]
15
+ attr_reader :access_flags
16
+ # @return [Array<FieldInfo>] static fields
17
+ attr_reader :static_fields
18
+ # @return [Array<FieldInfo>] instance fields
19
+ attr_reader :instance_fields
20
+ # @return [Array<MethodInfo>] direct methods
21
+ attr_reader :direct_methods
22
+ # @return [Array<MethodInfo>] virtual methods
23
+ attr_reader :virtual_methods
24
+
25
+ # @return [DexObject::ClassDataItem]
26
+ attr_reader :class_data
27
+ # @return [DexObject::ClassDefItem]
28
+ attr_reader :class_def
29
+
30
+ def name
31
+ @dex.type_resolve(@class_def[:class_idx])
32
+ end
33
+ def super_class
34
+ if @class_def[:superclass_idx] != NO_INDEX
35
+ @super_class = @dex.type_resolve(@class_def[:superclass_idx])
36
+ else
37
+ nil
38
+ end
39
+ end
40
+ # @param [Dex::ClassDefItem] class_def
41
+ # @param [Dex] dex dex class instance
42
+ def initialize(class_def, dex)
43
+ @class_def = class_def
44
+ @dex = dex
45
+ @access_flags = ClassAccessFlag.new(@class_def[:access_flags])
46
+ @class_data = @class_def.class_data_item
47
+ @static_fields = @instance_fields = @direct_methods = @virtual_methods = []
48
+ unless @class_data.nil?
49
+ @static_fields = cls2info(@class_data[:static_fields], FieldInfo, :field_idx_diff)
50
+ @instance_fields = cls2info(@class_data[:instance_fields], FieldInfo, :field_idx_diff)
51
+ @direct_methods = cls2info(@class_data[:direct_methods], MethodInfo, :method_idx_diff)
52
+ @virtual_methods = cls2info(@class_data[:virtual_methods], MethodInfo, :method_idx_diff)
53
+ end
54
+ end
55
+
56
+ # @return [String] class difinition
57
+ def definition
58
+ ret = "#{access_flags} class #{name}"
59
+ super_class.nil? ? ret : ret + " extends #{super_class}"
60
+ end
61
+
62
+ private
63
+ def cls2info(arr, cls, idx_key)
64
+ idx = 0
65
+ ret = []
66
+ arr.each do |item|
67
+ idx += item[idx_key]
68
+ ret << cls.new(item, idx, @dex)
69
+ end
70
+ ret
71
+ end
72
+ end
73
+
74
+ # field info object
75
+ # @!attribute [r] name
76
+ # @return [String] field name
77
+ # @!attribute [r] type
78
+ # @return [String] field type
79
+ class FieldInfo
80
+ # @return [ClassAccessFlag]
81
+ attr_reader :access_flags
82
+
83
+ def name
84
+ @dex.strings[@dex.field_ids[@field_id][:name_idx]]
85
+ end
86
+ def type
87
+ @dex.type_resolve(@dex.field_ids[@field_id][:type_idx])
88
+ end
89
+ def initialize(encoded_field, field_id, dex)
90
+ @dex = dex
91
+ @encoded_field = encoded_field
92
+ @field_id = field_id
93
+ @access_flags = ClassAccessFlag.new(encoded_field[:access_flags])
94
+ end
95
+
96
+ # @return [String] field definition
97
+ def definition
98
+ "#{@access_flags.to_s} #{type} #{name}"
99
+ end
100
+ end
101
+
102
+ # method info object
103
+ # @!attribute [r] name
104
+ # @return [String] method name
105
+ # @!attribute [r] ret_type
106
+ # @return [String] return type of the method
107
+ # @!attribute [r] parameters
108
+ # @return [Array<String>] method parameters
109
+ class MethodInfo
110
+ # @return [MethodAccessFlag]
111
+ attr_reader :access_flags
112
+
113
+ def initialize(encoded_method, method_id, dex)
114
+ @encoded_method = encoded_method
115
+ @method_id = method_id
116
+ @dex = dex
117
+ @access_flags = MethodAccessFlag.new(encoded_method[:access_flags])
118
+ end
119
+ def name
120
+ @dex.strings[@dex.method_ids[@method_id][:name_idx]]
121
+ end
122
+ def ret_type
123
+ @dex.type_resolve(proto[:return_type_idx])
124
+ end
125
+ def parameters
126
+ unless proto[:parameters_off] == 0
127
+ list = DexObject::TypeList.new(@dex.data, proto[:parameters_off])
128
+ list[:list].map { |item| @dex.type_resolve(item) }
129
+ else
130
+ []
131
+ end
132
+ end
133
+
134
+ # @return [String] method definition string
135
+ def definition
136
+ "#{access_flags.to_s} #{ret_type} #{name}(#{parameters.join(', ')});"
137
+ end
138
+
139
+ # @return [DexObject::CodeItem]
140
+ def code_item
141
+ @encoded_method.code_item
142
+ end
143
+
144
+ private
145
+ def proto
146
+ @dex.proto_ids[@dex.method_ids[@method_id][:proto_idx]]
147
+ end
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,45 @@
1
+ module Android
2
+ class Dex
3
+ class << self
4
+ # parse uleb128(unsigned integer) data
5
+ # @param [String] data target byte data
6
+ # @param [Integer] offset
7
+ # @return [Integer, Integer] parsed value and parsed byte length
8
+ # @see http://en.wikipedia.org/wiki/LEB128
9
+ def uleb128(data, offset=0)
10
+ result = 0
11
+ shift = 0
12
+ d = data[offset...data.size]
13
+ (0..4).each do |i|
14
+ byte = d.getbyte(i)
15
+ result |= ((byte & 0x7f) << shift)
16
+ return result, i+1 if ((byte & 0x80) == 0)
17
+ shift += 7
18
+ end
19
+ end
20
+ # parse uleb128 + 1 data
21
+ # @param [String] data target byte data
22
+ # @param [Integer] offset
23
+ # @return [Integer, Integer] parsed value and parsed byte length
24
+ def uleb128p1(data, offset=0)
25
+ ret, len = self.uleb128(data, offset)
26
+ return (ret - 1), len
27
+ end
28
+ # parse sleb128(signed integer) data
29
+ # @param [String] data target byte data
30
+ # @param [Integer] offset
31
+ # @return [Integer, Integer] parsed value and parsed byte length
32
+ def sleb128(data, offset=0)
33
+ result = 0
34
+ shift = 0
35
+ d = data[offset...data.size]
36
+ (0..4).each do |i|
37
+ byte = d.getbyte(i)
38
+ result |=((byte & 0x7F) << shift)
39
+ return (0 == (byte & 0x40) ? result : result - (1 << (shift+7))), i+1 if ((byte & 0x80) == 0)
40
+ shift += 7
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require 'rexml/document'
3
+
4
+ module Android
5
+ class Layout
6
+ # @return [Hash] { path => Layout }
7
+ def self.collect_layouts(apk)
8
+ targets = apk.find {|name, data| name =~ /^res\/layout\/*/ }
9
+ ret = {}
10
+ targets.each do |path|
11
+ data = apk.file(path)
12
+ data.force_encoding(Encoding::ASCII_8BIT)
13
+ ret[path] = nil
14
+ begin
15
+ ret[path] = Layout.new(data, path) if AXMLParser.axml?(data)
16
+ rescue => e
17
+ $stderr.puts e
18
+ end
19
+ end
20
+ ret
21
+ end
22
+
23
+ # @return [String] layout file path
24
+ attr_reader :path
25
+ # @return [REXML::Document] xml document object
26
+ attr_reader :doc
27
+
28
+ def initialize(data, path=nil)
29
+ @data = data
30
+ @path = path
31
+ @doc = AXMLParser.new(data).parse
32
+ end
33
+
34
+ # @return [String] xml string
35
+ def to_xml(indent=4)
36
+ xml = ''
37
+ formatter = REXML::Formatters::Pretty.new(indent)
38
+ formatter.write(@doc.root, xml)
39
+ xml
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,249 @@
1
+ require 'rexml/document'
2
+
3
+ module Android
4
+ # parsed AndroidManifest.xml class
5
+ # @see http://developer.android.com/guide/topics/manifest/manifest-intro.html
6
+ class Manifest
7
+ APPLICATION_TAG = '/manifest/application'
8
+
9
+ # <activity>, <service>, <receiver> or <provider> element in <application> element of the manifest file.
10
+ class Component
11
+ # component types
12
+ TYPES = ['service', 'activity', 'receiver', 'provider']
13
+
14
+ # the element is valid Component element or not
15
+ # @param [REXML::Element] elem xml element
16
+ # @return [Boolean]
17
+ def self.valid?(elem)
18
+ TYPES.include?(elem.name.downcase)
19
+ rescue => e
20
+ false
21
+ end
22
+
23
+ # @return [String] type string in TYPES
24
+ attr_reader :type
25
+ # @return [String] component name
26
+ attr_reader :name
27
+ # @return [Array<Manifest::IntentFilter>]
28
+ attr_reader :intent_filters
29
+ # @return [Array<Manifest::Meta>]
30
+ attr_reader :metas
31
+ # @return [REXML::Element]
32
+ attr_reader :elem
33
+
34
+
35
+ # @param [REXML::Element] elem target element
36
+ # @raise [ArgumentError] when elem is invalid.
37
+ def initialize(elem)
38
+ raise ArgumentError unless Component.valid?(elem)
39
+ @elem = elem
40
+ @type = elem.name
41
+ @name = elem.attributes['name']
42
+ @intent_filters = []
43
+ unless elem.elements['intent-filter'].nil?
44
+ elem.elements['intent-filter'].each do |e|
45
+ next unless e.instance_of? REXML::Element
46
+ @intent_filters << IntentFilter.parse(e)
47
+ end
48
+ end
49
+ @metas = []
50
+ elem.each_element('meta-data') do |e|
51
+ @metas << Meta.new(e)
52
+ end
53
+ end
54
+ end
55
+
56
+ # intent-filter element in components
57
+ module IntentFilter
58
+ # parse inside of intent-filter element
59
+ # @param [REXML::Element] elem target element
60
+ # @return [IntentFilter::Action, IntentFilter::Category, IntentFilter::Data]
61
+ # intent-filter element
62
+ def self.parse(elem)
63
+ case elem.name
64
+ when 'action'
65
+ Action.new(elem)
66
+ when 'category'
67
+ Category.new(elem)
68
+ when 'data'
69
+ Data.new(elem)
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ # intent-filter action class
76
+ class Action
77
+ # @return [String] action name of intent-filter
78
+ attr_reader :name
79
+ # @return [String] action type of intent-filter
80
+ attr_reader :type
81
+
82
+ def initialize(elem)
83
+ @type = 'action'
84
+ @name = elem.attributes['name']
85
+ end
86
+ end
87
+
88
+ # intent-filter category class
89
+ class Category
90
+ # @return [String] category name of intent-filter
91
+ attr_reader :name
92
+ # @return [String] category type of intent-filter
93
+ attr_reader :type
94
+
95
+ def initialize(elem)
96
+ @type = 'category'
97
+ @name = elem.attributes['name']
98
+ end
99
+ end
100
+
101
+ # intent-filter data class
102
+ class Data
103
+ # @return [String]
104
+ attr_reader :type
105
+ # @return [String]
106
+ attr_reader :host
107
+ # @return [String]
108
+ attr_reader :mime_type
109
+ # @return [String]
110
+ attr_reader :path
111
+ # @return [String]
112
+ attr_reader :path_pattern
113
+ # @return [String]
114
+ attr_reader :path_prefix
115
+ # @return [String]
116
+ attr_reader :port
117
+ # @return [String]
118
+ attr_reader :scheme
119
+
120
+ def initialize(elem)
121
+ @type = 'data'
122
+ @host = elem.attributes['host']
123
+ @mime_type = elem.attributes['mimeType']
124
+ @path = elem.attributes['path']
125
+ @path_pattern = elem.attributes['pathPattern']
126
+ @path_prefix = elem.attributes['pathPrefix']
127
+ @port = elem.attributes['port']
128
+ @scheme = elem.attributes['scheme']
129
+ end
130
+ end
131
+ end
132
+
133
+ # meta information class
134
+ class Meta
135
+ # @return [String]
136
+ attr_reader :name
137
+ # @return [String]
138
+ attr_reader :resource
139
+ # @return [String]
140
+ attr_reader :value
141
+ def initialize(elem)
142
+ @name = elem.attributes['name']
143
+ @resource = elem.attributes['resource']
144
+ @value = elem.attributes['value']
145
+ end
146
+ end
147
+
148
+ #################################
149
+ # Manifest class definitions
150
+ #################################
151
+ #
152
+ # @return [REXML::Document] manifest xml
153
+ attr_reader :doc
154
+
155
+ # @param [String] data binary data of AndroidManifest.xml
156
+ def initialize(data, rsc=nil)
157
+ parser = AXMLParser.new(data)
158
+ @doc = parser.parse
159
+ @rsc = rsc
160
+ end
161
+
162
+ # used permission array
163
+ # @return [Array<String>] permission names
164
+ # @note return empty array when the manifest includes no use-parmission element
165
+ def use_permissions
166
+ perms = []
167
+ @doc.each_element('/manifest/uses-permission') do |elem|
168
+ perms << elem.attributes['name']
169
+ end
170
+ perms.uniq
171
+ end
172
+
173
+ # @return [Array<Android::Manifest::Component>] all components in apk
174
+ # @note return empty array when the manifest include no components
175
+ def components
176
+ components = []
177
+ unless @doc.elements['/manifest/application'].nil?
178
+ @doc.elements['/manifest/application'].each do |elem|
179
+ components << Component.new(elem) if Component.valid?(elem)
180
+ end
181
+ end
182
+ components
183
+ end
184
+
185
+ # application package name
186
+ # @return [String]
187
+ def package_name
188
+ @doc.root.attributes['package']
189
+ end
190
+
191
+ # application version code
192
+ # @return [Integer]
193
+ def version_code
194
+ @doc.root.attributes['versionCode'].to_i
195
+ end
196
+
197
+ # application version name
198
+ # @return [String]
199
+ def version_name(lang=nil)
200
+ vername = @doc.root.attributes['versionName']
201
+ unless @rsc.nil?
202
+ if /^@(\w+\/\w+)|(0x[0-9a-fA-F]{8})$/ =~ vername
203
+ opts = {}
204
+ opts[:lang] = lang unless lang.nil?
205
+ vername = @rsc.find(vername, opts)
206
+ end
207
+ end
208
+ vername
209
+ end
210
+
211
+ # @return [Integer] minSdkVersion in uses element
212
+ def min_sdk_ver
213
+ @doc.elements['/manifest/uses-sdk'].attributes['minSdkVersion'].to_i
214
+ end
215
+
216
+ # application label
217
+ # @param [String] lang language code like 'ja', 'cn', ...
218
+ # @return [String] application label string(if resouce is provided), or label resource id
219
+ # @return [nil] when label is not found
220
+ # @since 0.5.1
221
+ def label(lang=nil)
222
+ label = @doc.elements['/manifest/application'].attributes['label']
223
+ if label.nil?
224
+ # application element has no label attributes.
225
+ # so looking for activites that has label attribute.
226
+ activities = @doc.elements['/manifest/application'].find{|e| e.name == 'activity' && !e.attributes['label'].nil? }
227
+ label = activities.nil? ? nil : activities.first.attributes['label']
228
+ end
229
+ unless @rsc.nil?
230
+ if /^@(\w+\/\w+)|(0x[0-9a-fA-F]{8})$/ =~ label
231
+ opts = {}
232
+ opts[:lang] = lang unless lang.nil?
233
+ label = @rsc.find(label, opts)
234
+ end
235
+ end
236
+ label
237
+ end
238
+
239
+ # return xml as string format
240
+ # @param [Integer] indent size(bytes)
241
+ # @return [String] raw xml string
242
+ def to_xml(indent=4)
243
+ xml =''
244
+ formatter = REXML::Formatters::Pretty.new(indent)
245
+ formatter.write(@doc.root, xml)
246
+ xml
247
+ end
248
+ end
249
+ end