device_api-android 1.0.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/51-android.rules +2 -0
- data/Gemfile.lock +13 -12
- data/device_api-android.gemspec +2 -2
- data/lib/device_api/android.rb +22 -2
- data/lib/device_api/android/aapt.rb +1 -17
- data/lib/device_api/android/adb.rb +107 -41
- data/lib/device_api/android/device.rb +123 -1
- data/lib/device_api/android/device/kindle.rb +14 -0
- data/lib/device_api/android/plugins/battery.rb +22 -0
- data/lib/device_api/android/plugins/disk.rb +29 -0
- data/lib/device_api/android/plugins/memory.rb +77 -0
- data/lib/device_api/android/signing.rb +2 -2
- data/spec/adb_spec.rb +19 -0
- data/spec/android_device_spec.rb +3 -3
- metadata +7 -6
- data/lib/device_api/android/devices/default.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d056fdaa82d61147897272f7a3df98afd5f8e4af
|
4
|
+
data.tar.gz: ea866645ded8c453e60ac3baec01ee929d983c2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 856ed8986cc089f9acc2098036a7e1deb95c93f86926f93bdbc2edd22f7cd5f5e4d7e255728049ee2d6321cde1910f54f810f1e3b5cdadba494385b86f9e40aa
|
7
|
+
data.tar.gz: 8b4a040ad4a5a01d652a7b1967ed3e1921726c5dcc1a6f916d76211616c465a9961607148e782f886444c245ac336127bf503c05b76684aa1d7ef1c2f1213caa
|
data/51-android.rules
CHANGED
@@ -50,6 +50,8 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="1949", ATTR{idProduct}=="000b", MODE="0666",
|
|
50
50
|
SUBSYSTEM=="usb", ATTR{idVendor}=="1949", ATTR{idProduct}=="000c", MODE="0666", OWNER="hive"
|
51
51
|
# Kindle Fire (Pink Case)
|
52
52
|
SUBSYSTEM=="usb", ATTR{idVendor}=="1949", ATTR{idProduct}=="00f2", MODE="0666", OWNER="hive"
|
53
|
+
# Kindle Fire HDX 8.9 (3rd Generation) OS 4.5.5
|
54
|
+
SUBSYSTEM=="usb", ATTR{idVendor}=="1949", ATTR{idProduct}=="000d", mode="0666", OWNER="hive"
|
53
55
|
# Sony Ericsson ST25i
|
54
56
|
SUBSYSTEM=="usb", ATTR{idVendor}=="0fce", ATTR{idProduct}=="5171", MODE="0666", OWNER="hive"
|
55
57
|
# Galaxy S2/S3
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
device_api (1.0.
|
4
|
+
device_api (1.0.1)
|
5
5
|
diff-lcs (1.2.5)
|
6
|
-
rspec (3.
|
7
|
-
rspec-core (~> 3.
|
8
|
-
rspec-expectations (~> 3.
|
9
|
-
rspec-mocks (~> 3.
|
10
|
-
rspec-core (3.
|
11
|
-
rspec-support (~> 3.
|
12
|
-
rspec-expectations (3.0
|
6
|
+
rspec (3.3.0)
|
7
|
+
rspec-core (~> 3.3.0)
|
8
|
+
rspec-expectations (~> 3.3.0)
|
9
|
+
rspec-mocks (~> 3.3.0)
|
10
|
+
rspec-core (3.3.1)
|
11
|
+
rspec-support (~> 3.3.0)
|
12
|
+
rspec-expectations (3.3.0)
|
13
13
|
diff-lcs (>= 1.2.0, < 2.0)
|
14
|
-
rspec-support (~> 3.
|
15
|
-
rspec-mocks (3.
|
16
|
-
|
17
|
-
|
14
|
+
rspec-support (~> 3.3.0)
|
15
|
+
rspec-mocks (3.3.1)
|
16
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
17
|
+
rspec-support (~> 3.3.0)
|
18
|
+
rspec-support (3.3.0)
|
18
19
|
|
19
20
|
PLATFORMS
|
20
21
|
ruby
|
data/device_api-android.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'device_api-android'
|
3
|
-
s.version = '1.
|
3
|
+
s.version = '1.1.2'
|
4
4
|
s.date = Time.now.strftime("%Y-%m-%d")
|
5
5
|
s.summary = 'Android Device Management API'
|
6
6
|
s.description = 'Android implementation of DeviceAPI'
|
7
|
-
s.authors = ['
|
7
|
+
s.authors = ['David Buckhurst','Jitesh Gosai', 'Jon Wilson']
|
8
8
|
s.email = 'david.buckhurst@bbc.co.uk'
|
9
9
|
s.files = `git ls-files`.split "\n"
|
10
10
|
s.homepage = 'https://github.com/bbc/device_api-android'
|
data/lib/device_api/android.rb
CHANGED
@@ -3,13 +3,21 @@ require 'device_api/android/adb'
|
|
3
3
|
require 'device_api/android/device'
|
4
4
|
require 'device_api/android/signing'
|
5
5
|
|
6
|
+
# Load plugins
|
7
|
+
require 'device_api/android/plugins/memory'
|
8
|
+
require 'device_api/android/plugins/battery'
|
9
|
+
require 'device_api/android/plugins/disk'
|
10
|
+
|
11
|
+
# Load additional device types
|
12
|
+
require 'device_api/android/device/kindle'
|
13
|
+
|
6
14
|
module DeviceAPI
|
7
15
|
module Android
|
8
16
|
# Returns array of connected android devices
|
9
17
|
def self.devices
|
10
18
|
ADB.devices.map do |d|
|
11
19
|
if d.keys.first && !d.keys.first.include?('?')
|
12
|
-
DeviceAPI::Android::Device.
|
20
|
+
DeviceAPI::Android::Device.create( self.get_device_type(d.keys.first), { serial: d.keys.first, state: d.values.first } )
|
13
21
|
end
|
14
22
|
end
|
15
23
|
end
|
@@ -20,7 +28,19 @@ module DeviceAPI
|
|
20
28
|
raise DeviceAPI::BadSerialString.new("serial was '#{serial.nil? ? 'nil' : serial}'")
|
21
29
|
end
|
22
30
|
state = ADB.get_state(serial)
|
23
|
-
DeviceAPI::Android::Device.
|
31
|
+
DeviceAPI::Android::Device.create( self.get_device_type(serial), { serial: serial, state: state })
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return the device type used in determining which Device Object to create
|
35
|
+
def self.get_device_type(serial)
|
36
|
+
return :default if Device.new(serial: serial).manufacturer.nil?
|
37
|
+
case Device.new(serial: serial).manufacturer.downcase
|
38
|
+
when 'amazon'
|
39
|
+
type = :kindle
|
40
|
+
else
|
41
|
+
type = :default
|
42
|
+
end
|
43
|
+
type
|
24
44
|
end
|
25
45
|
end
|
26
46
|
|
@@ -26,23 +26,7 @@ module DeviceAPI
|
|
26
26
|
|
27
27
|
fail result.stderr if result.exit != 0
|
28
28
|
|
29
|
-
|
30
|
-
results = []
|
31
|
-
lines.each do |l|
|
32
|
-
if /(.*): (.*)/.match(l)
|
33
|
-
# results.push(Regexp.last_match[1].strip => Regexp.last_match[2].strip)
|
34
|
-
values = {}
|
35
|
-
|
36
|
-
Regexp.last_match[2].strip.split(' ').each do |item| # split on an spaces
|
37
|
-
item = item.to_s.tr('\'', '') # trim off any excess single quotes
|
38
|
-
values[item.split('=')[0]] = item.split('=')[1] # split on the = and create a new hash
|
39
|
-
end
|
40
|
-
|
41
|
-
results << {Regexp.last_match[1].strip => values} # append the result tp new_result
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
45
|
-
results
|
29
|
+
result.stdout.scan(/(.*): (.*)/).map { |a,b| { a => Hash[b.split(' ').map { |c| c.tr('\'','').split('=') }] } }
|
46
30
|
end
|
47
31
|
|
48
32
|
end
|
@@ -18,17 +18,7 @@ module DeviceAPI
|
|
18
18
|
result = execute_with_timeout_and_retry('adb devices')
|
19
19
|
|
20
20
|
raise ADBCommandError.new(result.stderr) if result.exit != 0
|
21
|
-
|
22
|
-
lines = result.stdout.split("\n")
|
23
|
-
results = []
|
24
|
-
|
25
|
-
lines.shift # Drop the message line
|
26
|
-
lines.each do |l|
|
27
|
-
if /(.*)\t(.*)/.match(l)
|
28
|
-
results.push(Regexp.last_match[1].strip => Regexp.last_match[2].strip)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
results
|
21
|
+
result.stdout.scan(/(.*)\t(.*)/).map { |a,b| {a => b}}
|
32
22
|
end
|
33
23
|
|
34
24
|
# Retrieve device state for a single device
|
@@ -54,13 +44,7 @@ module DeviceAPI
|
|
54
44
|
|
55
45
|
lines = result.stdout.split("\n")
|
56
46
|
|
57
|
-
|
58
|
-
lines.each do |l|
|
59
|
-
if /\[(.*)\]:\s+\[(.*)\]/.match(l)
|
60
|
-
props[Regexp.last_match[1]] = Regexp.last_match[2]
|
61
|
-
end
|
62
|
-
end
|
63
|
-
props
|
47
|
+
process_dumpsys('\[(.*)\]:\s+\[(.*)\]', lines)
|
64
48
|
end
|
65
49
|
|
66
50
|
# Get the 'input' information from dumpsys
|
@@ -68,14 +52,7 @@ module DeviceAPI
|
|
68
52
|
# @return (Hash) hash containing input information from dumpsys
|
69
53
|
def self.getdumpsys(serial)
|
70
54
|
lines = dumpsys(serial, 'input')
|
71
|
-
|
72
|
-
props = {}
|
73
|
-
lines.each do |l|
|
74
|
-
if /(.*):\s+(.*)/.match(l)
|
75
|
-
props[Regexp.last_match[1]] = Regexp.last_match[2]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
props
|
55
|
+
process_dumpsys('(.*):\s+(.*)', lines)
|
79
56
|
end
|
80
57
|
|
81
58
|
# Get the 'iphonesubinfo' from dumpsys
|
@@ -83,16 +60,52 @@ module DeviceAPI
|
|
83
60
|
# @return (Hash) hash containing iphonesubinfo information from dumpsys
|
84
61
|
def self.getphoneinfo(serial)
|
85
62
|
lines = dumpsys(serial, 'iphonesubinfo')
|
63
|
+
process_dumpsys('(.*) =\s+(.*)', lines)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get the 'battery' information from dumpsys
|
67
|
+
# @param [String] serial serial number of device
|
68
|
+
# @return [Hash] hash containing battery information from dumpsys
|
69
|
+
def self.get_battery_info(serial)
|
70
|
+
lines = dumpsys(serial, 'battery')
|
71
|
+
process_dumpsys('(.*):\s+(.*)', lines)
|
72
|
+
end
|
86
73
|
|
74
|
+
# Processes the results from dumpsys to format them into a hash
|
75
|
+
# @param [String] regex_string regex string used to separate the results from the keys
|
76
|
+
# @param [Array] data data returned from dumpsys
|
77
|
+
# @return [Hash] hash containing the keys and values as distinguished by the supplied regex
|
78
|
+
def self.process_dumpsys(regex_string, data)
|
87
79
|
props = {}
|
88
|
-
|
89
|
-
|
80
|
+
regex = Regexp.new(regex_string)
|
81
|
+
data.each do |line|
|
82
|
+
if regex.match(line)
|
90
83
|
props[Regexp.last_match[1]] = Regexp.last_match[2]
|
91
84
|
end
|
92
85
|
end
|
86
|
+
|
93
87
|
props
|
94
88
|
end
|
95
89
|
|
90
|
+
# Get the 'power' information from dumpsys
|
91
|
+
# @param [String] serial serial number of device
|
92
|
+
# @return [Hash] hash containing power information from dumpsys
|
93
|
+
def self.getpowerinfo(serial)
|
94
|
+
lines = dumpsys(serial, 'power')
|
95
|
+
process_dumpsys('(.*)=(.*)', lines)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.get_device_dpi(serial)
|
99
|
+
lines = dumpsys(serial, 'window')
|
100
|
+
dpi = nil
|
101
|
+
lines.each do |line|
|
102
|
+
if /sw(\d*)dp/.match(line)
|
103
|
+
dpi = Regexp.last_match[1]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
dpi
|
107
|
+
end
|
108
|
+
|
96
109
|
# Returns the 'dumpsys' information from the specified device
|
97
110
|
# @param serial serial number of device
|
98
111
|
# @return (Array) array of results from adb shell dumpsys
|
@@ -108,18 +121,8 @@ module DeviceAPI
|
|
108
121
|
# @option options [String] :serial serial number of device
|
109
122
|
# @return (String) return result from adb install command
|
110
123
|
def self.install_apk(options = {})
|
111
|
-
|
112
|
-
|
113
|
-
result = execute("adb -s #{serial} install #{apk}")
|
114
|
-
|
115
|
-
raise ADBCommandError.new(result.stderr) if result.exit != 0
|
116
|
-
|
117
|
-
lines = result.stdout.split("\n").map { |line| line.strip }
|
118
|
-
# lines.each do |line|
|
119
|
-
# res=:success if line=='Success'
|
120
|
-
# end
|
121
|
-
|
122
|
-
lines.last
|
124
|
+
options[:action] = :install
|
125
|
+
change_apk(options)
|
123
126
|
end
|
124
127
|
|
125
128
|
# Uninstalls a specified package from a specified device
|
@@ -128,9 +131,27 @@ module DeviceAPI
|
|
128
131
|
# @option options [String] :serial serial number of device
|
129
132
|
# @return (String) return result from adb uninstall command
|
130
133
|
def self.uninstall_apk(options = {})
|
134
|
+
options[:action] = :uninstall
|
135
|
+
change_apk(options)
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.change_apk(options = {})
|
131
139
|
package_name = options[:package_name]
|
140
|
+
apk = options[:apk]
|
132
141
|
serial = options[:serial]
|
133
|
-
|
142
|
+
action = options[:action]
|
143
|
+
|
144
|
+
case action
|
145
|
+
when :install
|
146
|
+
command = "adb -s #{serial} install #{apk}"
|
147
|
+
when :uninstall
|
148
|
+
command = "adb -s #{serial} uninstall #{package_name}"
|
149
|
+
else
|
150
|
+
raise ADBCommandError.new('No action specified')
|
151
|
+
end
|
152
|
+
|
153
|
+
result = execute(command)
|
154
|
+
|
134
155
|
raise ADBCommandError.new(result.stderr) if result.exit != 0
|
135
156
|
|
136
157
|
lines = result.stdout.split("\n").map { |line| line.strip }
|
@@ -203,6 +224,51 @@ module DeviceAPI
|
|
203
224
|
execute(cmd)
|
204
225
|
end
|
205
226
|
|
227
|
+
# Returns wifi status and access point name
|
228
|
+
# @param serial serial number of device
|
229
|
+
# @example
|
230
|
+
# DeviceAPI::ADB.wifi(serial)
|
231
|
+
def self.wifi(serial)
|
232
|
+
result = execute("adb -s #{serial} shell dumpsys wifi | grep mNetworkInfo")
|
233
|
+
if result.exit != 0
|
234
|
+
raise ADBCommandError.new(result.stderr)
|
235
|
+
else
|
236
|
+
result = {:status => result.stdout.match("state:(.*?),")[1].strip, :access_point => result.stdout.match("extra:(.*?),")[1].strip.gsub(/"/,'')}
|
237
|
+
end
|
238
|
+
result
|
239
|
+
end
|
240
|
+
|
241
|
+
# Sends a key event to the specified device
|
242
|
+
# @param [String] serial serial number of device
|
243
|
+
# @param [String] keyevent keyevent to send to the device
|
244
|
+
def self.keyevent(serial, keyevent)
|
245
|
+
result = execute("adb -s #{serial} shell input keyevent #{keyevent}")
|
246
|
+
raise ADBCommandError.new(result.stderr) if result.exit != 0
|
247
|
+
end
|
248
|
+
|
249
|
+
# Sends a swipe command to the specified device
|
250
|
+
# @param [String] serial serial number of the device
|
251
|
+
# @param [Hash] coords hash of coordinates to swipe from / to
|
252
|
+
# @option coords [String] :x_from (0) Coordinate to start from on the X axis
|
253
|
+
# @option coords [String] :x_to (0) Coordinate to end on on the X axis
|
254
|
+
# @option coords [String] :y_from (0) Coordinate to start from on the Y axis
|
255
|
+
# @option coords [String] :y_to (0) Coordinate to end on on the Y axis
|
256
|
+
def self.swipe(serial, coords = {x_from: 0, x_to: 0, y_from: 0, y_to: 0 })
|
257
|
+
execute("adb -s #{serial} shell input swipe #{coords[:x_from]} #{coords[:x_to]} #{coords[:y_from]} #{coords[:y_to]}")
|
258
|
+
raise ADBCommandError.new(result.stderr) if result.exit != 0
|
259
|
+
end
|
260
|
+
|
261
|
+
# Starts intent using adb
|
262
|
+
# Returns stdout
|
263
|
+
# @param serial serial number of device
|
264
|
+
# @param command -option activity
|
265
|
+
# @example
|
266
|
+
# DeviceAPI::ADB.am(serial, "-a android.intent.action.MAIN -n com.android.settings/.wifi.WifiSettings")
|
267
|
+
def self.am(serial, command)
|
268
|
+
result = execute("adb -s #{serial} shell am start #{command}")
|
269
|
+
raise ADBCommandError.new(result.stderr) if result.exit != 0
|
270
|
+
return result.stdout
|
271
|
+
end
|
206
272
|
end
|
207
273
|
|
208
274
|
# ADB Error class
|
@@ -9,6 +9,21 @@ module DeviceAPI
|
|
9
9
|
module Android
|
10
10
|
# Device class used for containing the accessors of the physical device information
|
11
11
|
class Device < DeviceAPI::Device
|
12
|
+
|
13
|
+
@@subclasses; @@subclasses = {}
|
14
|
+
|
15
|
+
# Called by any inheritors to register themselves with the parent class
|
16
|
+
def self.inherited(klass)
|
17
|
+
key = /::([^:]+)$/.match(klass.to_s.downcase)[1].to_sym
|
18
|
+
@@subclasses[key] = klass
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns an object of the specified type, if it exists. Defaults to returning self
|
22
|
+
def self.create(type, options = {} )
|
23
|
+
return @@subclasses[type.to_sym].new(options) if @@subclasses[type.to_sym]
|
24
|
+
return self.new(options)
|
25
|
+
end
|
26
|
+
|
12
27
|
def initialize(options = {})
|
13
28
|
@serial = options[:serial]
|
14
29
|
@state = options[:state]
|
@@ -58,13 +73,25 @@ module DeviceAPI
|
|
58
73
|
get_prop('ro.build.version.release')
|
59
74
|
end
|
60
75
|
|
76
|
+
# Return the battery level
|
77
|
+
# @return (String) device battery level
|
78
|
+
def battery_level
|
79
|
+
get_battery_info['level']
|
80
|
+
end
|
81
|
+
|
82
|
+
# Is the device currently being powered?
|
83
|
+
# @return (Boolean) true if it is being powered in some way, false if it is unpowered
|
84
|
+
def powered?
|
85
|
+
!get_battery_info.select { |keys| keys.include?('powered')}.select { |_,v| v == 'true' }.empty?
|
86
|
+
end
|
87
|
+
|
61
88
|
# Return the device orientation
|
62
89
|
# @return (String) current device orientation
|
63
90
|
def orientation
|
64
91
|
res = get_dumpsys('SurfaceOrientation')
|
65
92
|
|
66
93
|
case res
|
67
|
-
when '0'
|
94
|
+
when '0','2'
|
68
95
|
:portrait
|
69
96
|
when '1', '3'
|
70
97
|
:landscape
|
@@ -141,8 +168,94 @@ module DeviceAPI
|
|
141
168
|
get_phoneinfo['Device ID']
|
142
169
|
end
|
143
170
|
|
171
|
+
# Get the memory information for the current device
|
172
|
+
# @return [DeviceAPI::Android::Plugins::Memory] the memory plugin containing relevant information
|
173
|
+
def memory
|
174
|
+
get_memory_info
|
175
|
+
end
|
176
|
+
|
177
|
+
def battery
|
178
|
+
get_battery_info
|
179
|
+
end
|
180
|
+
|
181
|
+
# Check if the devices screen is currently turned on
|
182
|
+
# @return [Boolean] true if the screen is on, otherwise false
|
183
|
+
def screen_on?
|
184
|
+
return true if get_powerinfo['mScreenOn'].to_s.downcase == 'true'
|
185
|
+
false
|
186
|
+
end
|
187
|
+
|
188
|
+
# Unlock the device by sending a wakeup command
|
189
|
+
def unlock
|
190
|
+
ADB.keyevent(serial, '26') unless screen_on?
|
191
|
+
end
|
192
|
+
|
193
|
+
# Return the DPI of the attached device
|
194
|
+
# @return [String] DPI of attached device
|
195
|
+
def dpi
|
196
|
+
get_dpi(serial)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Return the device type based on the DPI
|
200
|
+
# @return [Symbol] :tablet or :mobile based upon the devices DPI
|
201
|
+
def type
|
202
|
+
if get_dpi.to_i > 533
|
203
|
+
:tablet
|
204
|
+
else
|
205
|
+
:mobile
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Returns wifi status and access point name
|
210
|
+
# @return [Hash] :status and :access_point
|
211
|
+
def wifi_status
|
212
|
+
ADB.wifi(serial)
|
213
|
+
end
|
214
|
+
|
215
|
+
def battery_info
|
216
|
+
ADB.get_battery_info(serial)
|
217
|
+
end
|
218
|
+
|
219
|
+
# @param [String] command to start the intent
|
220
|
+
# Return the stdout of executed intent
|
221
|
+
# @return [String] stdout
|
222
|
+
def start_intent(command)
|
223
|
+
ADB.am(serial,command)
|
224
|
+
end
|
225
|
+
|
226
|
+
#Reboots the device
|
227
|
+
def reboot
|
228
|
+
ADB.reboot(serial)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Returns disk status
|
232
|
+
# @return [Hash] containing disk statistics
|
233
|
+
def diskstat
|
234
|
+
get_disk_info
|
235
|
+
end
|
236
|
+
|
237
|
+
# Returns the device uptime
|
238
|
+
def uptime
|
239
|
+
ADB.get_uptime(serial)
|
240
|
+
end
|
241
|
+
|
144
242
|
private
|
145
243
|
|
244
|
+
def get_disk_info
|
245
|
+
@diskstat = DeviceAPI::Android::Plugin::Disk.new(serial: serial) unless @diskstat
|
246
|
+
@diskstat.process_stats
|
247
|
+
end
|
248
|
+
|
249
|
+
def get_battery_info
|
250
|
+
@battery = DeviceAPI::Android::Plugin::Battery.new(serial: serial) unless @battery
|
251
|
+
@battery
|
252
|
+
end
|
253
|
+
|
254
|
+
def get_memory_info
|
255
|
+
@memory = DeviceAPI::Android::Plugin::Memory.new(serial: serial) unless @memory
|
256
|
+
@memory
|
257
|
+
end
|
258
|
+
|
146
259
|
def get_app_props(key)
|
147
260
|
unless @app_props
|
148
261
|
@app_props = AAPT.get_app_props(@apk)
|
@@ -162,6 +275,11 @@ module DeviceAPI
|
|
162
275
|
@props[key]
|
163
276
|
end
|
164
277
|
|
278
|
+
def get_powerinfo
|
279
|
+
ADB.getpowerinfo(serial)
|
280
|
+
end
|
281
|
+
|
282
|
+
|
165
283
|
def get_phoneinfo
|
166
284
|
ADB.getphoneinfo(serial)
|
167
285
|
end
|
@@ -173,6 +291,10 @@ module DeviceAPI
|
|
173
291
|
def uninstall_apk(package_name)
|
174
292
|
ADB.uninstall_apk(package_name: package_name, serial: serial)
|
175
293
|
end
|
294
|
+
|
295
|
+
def get_dpi
|
296
|
+
ADB.get_device_dpi(serial)
|
297
|
+
end
|
176
298
|
end
|
177
299
|
end
|
178
300
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module DeviceAPI
|
2
|
+
module Android
|
3
|
+
# Kindle specific device class
|
4
|
+
class Kindle < Device
|
5
|
+
# On non-Kindle devices, if a device is locked without a password (i.e. 'Swipe to unlock'), then
|
6
|
+
# you can unlock that device by broadcasting a 'WakeUp' intent. On Kindle devices, this does not
|
7
|
+
# work due to Amazons implementation of the Keyguard.
|
8
|
+
def unlock
|
9
|
+
ADB.keyevent(serial, '26') unless screen_on?
|
10
|
+
ADB.swipe(serial, { x_from: 900, x_to: 300, y_from: 100, y_to: 100 } )
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module DeviceAPI
|
2
|
+
module Android
|
3
|
+
module Plugin
|
4
|
+
class Battery
|
5
|
+
attr_reader :current_temp, :max_temp, :max_current, :voltage, :level, :health, :status
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
serial = options[:serial]
|
9
|
+
props = ADB.get_battery_info(serial)
|
10
|
+
@current_temp = props["temperature"]
|
11
|
+
@max_temp = props["mBatteryMaxTemp"]
|
12
|
+
@max_current = props["mBatteryMaxCurrent"]
|
13
|
+
@voltage = props["voltage"]
|
14
|
+
@level = props["level"]
|
15
|
+
@health = props["health"]
|
16
|
+
@status = props["status"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module DeviceAPI
|
2
|
+
module Android
|
3
|
+
module Plugin
|
4
|
+
class Disk
|
5
|
+
|
6
|
+
attr_reader :serial
|
7
|
+
def initialize(options = {})
|
8
|
+
@serial = options[:serial]
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_stats(options = {})
|
12
|
+
disk_info = {}
|
13
|
+
stats = options[:data] || ADB.dumpsys(@serial, 'diskstats')
|
14
|
+
stats.each do |stat|
|
15
|
+
if /(.*)-.*:\s(.*)\s\/\s([0-9]*[A-Z])\s[a-z]*\s=\s([0-9]*%)/.match(stat)
|
16
|
+
disk_info["#{Regexp.last_match[1].downcase}_total"] = Regexp.last_match[3]
|
17
|
+
disk_info["#{Regexp.last_match[1].downcase}_free"] = Regexp.last_match[4]
|
18
|
+
disk_info["#{Regexp.last_match[1].downcase}_used"] = Regexp.last_match[2]
|
19
|
+
elsif /(.*):\s(\S*)/.match(stat)
|
20
|
+
disk_info[Regexp.last_match[1].downcase] = Regexp.last_match[2]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
disk_info
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module DeviceAPI
|
2
|
+
module Android
|
3
|
+
# Plugins contain extra information about the attached device(s)
|
4
|
+
module Plugin
|
5
|
+
# Class used to provide information about process memory usage
|
6
|
+
# and device memory usage
|
7
|
+
class Memory
|
8
|
+
|
9
|
+
# Class used for holding process information
|
10
|
+
class MemInfo
|
11
|
+
attr_reader :process, :memory, :pid
|
12
|
+
def initialize(options = {})
|
13
|
+
@process = options[:process]
|
14
|
+
@memory = options[:memory]
|
15
|
+
@pid = options[:pid]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Class used for storing process information
|
20
|
+
class RAM
|
21
|
+
attr_accessor :total, :free, :used, :lost, :tuning
|
22
|
+
def initialize(options = {})
|
23
|
+
@total = options[:total]
|
24
|
+
@free = options[:free]
|
25
|
+
@used = options[:used]
|
26
|
+
@lost = options[:lost]
|
27
|
+
@tuning = options[:tuning]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :processes, :mem_info
|
32
|
+
|
33
|
+
def initialize(options = {})
|
34
|
+
@serial = options[:serial]
|
35
|
+
info = options[:data] || ADB.dumpsys(@serial, 'meminfo')
|
36
|
+
process_data(info)
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_data(memory_info)
|
40
|
+
groups = memory_info.chunk { |a| a == '' }.reject { |a,_| a }.map { |_,b| b }
|
41
|
+
|
42
|
+
raise 'A different ADB result has been received' unless groups[1].first == 'Total PSS by process:'
|
43
|
+
@processes = []
|
44
|
+
process_total_pss_by_process(groups[1])
|
45
|
+
process_ram_info(groups[4])
|
46
|
+
end
|
47
|
+
|
48
|
+
def update
|
49
|
+
meminfo = ADB.dumpsys(@serial, 'meminfo')
|
50
|
+
process_data(meminfo)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Processes memory used by each running process
|
54
|
+
def process_total_pss_by_process(data)
|
55
|
+
data.each do |l|
|
56
|
+
if /(.*):\s+(.*)\s+\(.*pid\s+(\S*).*\)/.match(l)
|
57
|
+
@processes << MemInfo.new(process: Regexp.last_match[2], memory: Regexp.last_match[1], pid: Regexp.last_match[3] )
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Processes memory used by the device
|
63
|
+
def process_ram_info(data)
|
64
|
+
ram_info = {}
|
65
|
+
data.each do |l|
|
66
|
+
if /Tuning:\s+(.*)/.match(l)
|
67
|
+
ram_info['tuning'] = Regexp.last_match[1]
|
68
|
+
elsif /(.*):\s(-?[0-9]*\s\S*)/.match(l)
|
69
|
+
ram_info[Regexp.last_match[1].downcase] = Regexp.last_match[2]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
@mem_info = RAM.new(total: ram_info['total ram'], free: ram_info['free ram'], used: ram_info['used ram'], lost: ram_info['lost'], tuning: ram_info['tuning'])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -54,7 +54,7 @@ module DeviceAPI
|
|
54
54
|
# @param [String] apk_path full path to apk to check
|
55
55
|
# @return returns false if the apk is unsigned, true if it is signed
|
56
56
|
def self.is_apk_signed?(apk_path)
|
57
|
-
result = execute("aapt list #{apk_path} | grep '^META-INF
|
57
|
+
result = execute("aapt list #{apk_path} | grep '^META-INF\/.*'")
|
58
58
|
return false if result.stdout.empty?
|
59
59
|
true
|
60
60
|
end
|
@@ -63,7 +63,7 @@ module DeviceAPI
|
|
63
63
|
# @param [String] apk_path full path to the apk
|
64
64
|
# @return [Boolean, Exception] returns true if the apk is successfully unsigned, otherwise an exception is raised
|
65
65
|
def self.unsign_apk(apk_path)
|
66
|
-
file_list = execute("aapt list #{apk_path} | grep '^META-INF
|
66
|
+
file_list = execute("aapt list #{apk_path} | grep '^META-INF\/.*'")
|
67
67
|
result = execute("aapt remove #{apk_path} #{file_list.stdout.split(/\s+/).join(' ')}")
|
68
68
|
raise SigningCommandError.new(result.stderr) if result.exit != 0
|
69
69
|
true
|
data/spec/adb_spec.rb
CHANGED
@@ -135,4 +135,23 @@ _______________________________________________________
|
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
+
describe ".wifi" do
|
139
|
+
it "returns wifi info" do
|
140
|
+
out= <<_______________________________________________________
|
141
|
+
mNetworkInfo [type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: "TVMP-DevNet", roaming: false, failover: false, isAvailable: true, isConnectedToProvisioningNetwork: false]
|
142
|
+
_______________________________________________________
|
143
|
+
allow(Open3).to receive(:capture3) { [out, '', $STATUS_ZERO] }
|
144
|
+
expect( DeviceAPI::Android::ADB.wifi('12345').class).to eq(Hash)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe ".am" do
|
149
|
+
it "returns the stdout" do
|
150
|
+
out= <<_______________________________________________________
|
151
|
+
Starting: Intent { act=android.intent.action.MAIN cmp=com.android.settings/.wifi.WifiSettings }
|
152
|
+
_______________________________________________________
|
153
|
+
allow(Open3).to receive(:capture3) { [out, '', $STATUS_ZERO] }
|
154
|
+
expect( DeviceAPI::Android::ADB.am('03157df373208426' ,'12345').class).to eq(String)
|
155
|
+
end
|
156
|
+
end
|
138
157
|
end
|
data/spec/android_device_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe DeviceAPI::Android do
|
|
7
7
|
|
8
8
|
it 'Returns an empty array when no devices are connected' do
|
9
9
|
out = <<_______________________________________________________
|
10
|
-
|
10
|
+
List of devices attached
|
11
11
|
|
12
12
|
_______________________________________________________
|
13
13
|
allow(Open3).to receive(:capture3) { [out, '', $STATUS_ZERO] }
|
@@ -16,8 +16,8 @@ _______________________________________________________
|
|
16
16
|
|
17
17
|
it "returns an array with a single item when there's one device attached" do
|
18
18
|
out = <<_______________________________________________________
|
19
|
-
|
20
|
-
|
19
|
+
List of devices attached
|
20
|
+
SH34RW905290 device
|
21
21
|
|
22
22
|
_______________________________________________________
|
23
23
|
allow(Open3).to receive(:capture3) { [out, '', $STATUS_ZERO] }
|
metadata
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: device_api-android
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- BBC
|
8
7
|
- David Buckhurst
|
9
8
|
- Jitesh Gosai
|
10
9
|
- Jon Wilson
|
11
10
|
autorequire:
|
12
11
|
bindir: bin
|
13
12
|
cert_chain: []
|
14
|
-
date: 2015-
|
13
|
+
date: 2015-10-06 00:00:00.000000000 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: device_api
|
@@ -64,7 +63,10 @@ files:
|
|
64
63
|
- lib/device_api/android/aapt.rb
|
65
64
|
- lib/device_api/android/adb.rb
|
66
65
|
- lib/device_api/android/device.rb
|
67
|
-
- lib/device_api/android/
|
66
|
+
- lib/device_api/android/device/kindle.rb
|
67
|
+
- lib/device_api/android/plugins/battery.rb
|
68
|
+
- lib/device_api/android/plugins/disk.rb
|
69
|
+
- lib/device_api/android/plugins/memory.rb
|
68
70
|
- lib/device_api/android/signing.rb
|
69
71
|
- spec/adb_spec.rb
|
70
72
|
- spec/android_device_spec.rb
|
@@ -90,9 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
92
|
version: '0'
|
91
93
|
requirements: []
|
92
94
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.4.
|
95
|
+
rubygems_version: 2.4.8
|
94
96
|
signing_key:
|
95
97
|
specification_version: 4
|
96
98
|
summary: Android Device Management API
|
97
99
|
test_files: []
|
98
|
-
has_rdoc:
|
File without changes
|