Class: DeviceAPI::Android::ADB

Inherits:
Execution
  • Object
show all
Defined in:
lib/device_api/android/adb.rb

Overview

Namespace for all methods encapsulating adb calls

Class Method Summary collapse

Class Method Details

.am(qualifier, command) ⇒ Object

Starts intent using adb Returns stdout DeviceAPI::ADB.am(qualifier, “start -a android.intent.action.MAIN -n com.android.settings/.wifi.WifiSettings”)

Parameters:

  • qualifier

    qualifier of device

  • command

    -option activity



335
336
337
# File 'lib/device_api/android/adb.rb', line 335

def self.am(qualifier, command)
  shell(qualifier, "am #{command}").stdout
end

.block_package(qualifier, package) ⇒ Object

Blocks a package, used on Android versions less than KitKat Returns boolean

Parameters:

  • qualifier

    qualifier of device

  • package

    to block



351
352
353
354
# File 'lib/device_api/android/adb.rb', line 351

def self.block_package(qualifier, package)
  result = pm(qualifier, "block #{package}")
  result.include?('true')
end

.change_apk(options = {}) ⇒ Object

Raises:



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/device_api/android/adb.rb', line 149

def self.change_apk(options = {})
  package_name = options[:package_name]
  apk = options[:apk]
  qualifier = options[:qualifier]
  action = options[:action]

  case action
    when :install
      command = "adb -s #{qualifier} install #{apk}"
    when :uninstall
      command = "adb -s #{qualifier} uninstall #{package_name}"
    else
      raise ADBCommandError.new('No action specified')
  end

  result = execute(command)

  raise ADBCommandError.new(result.stderr) if result.exit != 0

  lines = result.stdout.split("\n").map { |line| line.strip }

  lines.last
end

.check_ip_address(ip_address_and_port) ⇒ Object



365
366
367
368
369
# File 'lib/device_api/android/adb.rb', line 365

def self.check_ip_address(ip_address_and_port)
  unless ip_address_and_port =~ /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3}):[0-9]+\Z/ 
       raise ADBCommandError.new("Invalid IP address and port #{ip_address_and_port}")
  end
end

.connect(ip_address, port = 5555) ⇒ Object

Connects to remote android device

Examples:

DeviceAPI::ADB.connect(ip_address, port)  

Parameters:

  • ip_address (String)
  • port (String) (defaults to: 5555)


255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/device_api/android/adb.rb', line 255

def self.connect(ip_address, port=5555)
  ip_address_and_port = "#{ip_address}:#{port}"
  check_ip_address(ip_address_and_port)
  cmd = "adb connect #{ip_address_and_port}"
  result = execute(cmd)
  if result.stdout.to_s =~ /.*already connected to.*/
    raise DeviceAlreadyConnectedError.new("Device #{ip_address_and_port} already connected")
  else 
    unless result.stdout.to_s =~ /.*connected to.*/
      raise ADBCommandError.new("Unable to adb connect to #{ip_address_and_port} result was: #{result.stdout}")
    end
  end 
end

.devicesArray

Returns an array representing connected devices DeviceAPI::ADB.devices #=> { ‘1232132’ => ‘device’ }

Returns:

  • (Array)

    list of attached devices

Raises:



17
18
19
20
21
22
# File 'lib/device_api/android/adb.rb', line 17

def self.devices
  result = execute_with_timeout_and_retry('adb devices')

  raise ADBCommandError.new(result.stderr) if result.exit != 0
  result.stdout.scan(/(.*)\t(.*)/).map { |a,b| {a => b}}
end

.disconnect(ip_address, port = 5555) ⇒ Object

Disconnects from remote android device

Examples:

DeviceAPI::ADB.disconnect(ip_address, port) 

Parameters:

  • ip_address (String)
  • port (String) (defaults to: 5555)


274
275
276
277
278
279
280
281
282
# File 'lib/device_api/android/adb.rb', line 274

def self.disconnect(ip_address, port=5555)
  ip_address_and_port = "#{ip_address}:#{port}"
  check_ip_address(ip_address_and_port)
  cmd = "adb disconnect #{ip_address_and_port}"
  result = execute(cmd)
  unless result.exit == 0
    raise ADBCommandError.new("Unable to adb disconnect to #{ip_address_and_port} result was: #{result.stdout}")
  end
end

.dumpsys(qualifier, command) ⇒ Array

Returns the ‘dumpsys’ information from the specified device

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (Array)

    array of results from adb shell dumpsys



124
125
126
127
# File 'lib/device_api/android/adb.rb', line 124

def self.dumpsys(qualifier, command)
  result = shell(qualifier, "dumpsys #{command}")
  result.stdout.split("\n").map { |line| line.strip }
end

.get_battery_info(qualifier) ⇒ Hash

Get the ‘battery’ information from dumpsys

Parameters:

  • qualifier (String)

    qualifier of device

Returns:

  • (Hash)

    hash containing battery information from dumpsys



67
68
69
70
# File 'lib/device_api/android/adb.rb', line 67

def self.get_battery_info(qualifier)
  lines = dumpsys(qualifier, 'battery')
  process_dumpsys('(.*):\s+(.*)', lines)
end

.get_device_dpi(qualifier) ⇒ Object



110
111
112
113
114
115
116
117
118
119
# File 'lib/device_api/android/adb.rb', line 110

def self.get_device_dpi(qualifier)
  lines = dumpsys(qualifier, 'window')
  dpi = nil
  lines.each do |line|
    if /sw(\d*)dp/.match(line)
      dpi = Regexp.last_match[1]
    end
  end
  dpi
end

.get_network_info(qualifier) ⇒ Object

Get the network information



78
79
80
81
82
83
84
# File 'lib/device_api/android/adb.rb', line 78

def self.get_network_info(qualifier)
  lines = shell(qualifier, 'netcfg')
  lines.stdout.split("\n").map do |a|
    b = a.split(" ")
    { name: b[0], ip: b[2].split('/')[0], mac: b[4] }
  end
end

.get_network_interface(qualifier, interface) ⇒ Object



72
73
74
75
# File 'lib/device_api/android/adb.rb', line 72

def self.get_network_interface(qualifier, interface)
  result = shell(qualifier, "ifconfig #{interface}")
  result.stdout
end

.get_state(qualifier) ⇒ String

Retrieve device state for a single device

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (String)

    device state

Raises:



27
28
29
30
31
32
33
34
35
# File 'lib/device_api/android/adb.rb', line 27

def self.get_state(qualifier)
  result = execute('adb -s #{qualifier} get-state')

  raise ADBCommandError.new(result.stderr) if result.exit != 0

  lines = result.stdout.split("\n")
  /(.*)/.match(lines.last)
  Regexp.last_match[0].strip
end

.get_uptime(qualifier) ⇒ Float

Returns the uptime of the specified device

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (Float)

    uptime in seconds



176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/device_api/android/adb.rb', line 176

def self.get_uptime(qualifier)
  result = shell(qualifier, 'cat /proc/uptime')

  lines = result.stdout.split("\n")
  uptime = 0
  lines.each do |l|
    if /([\d.]*)\s+[\d.]*/.match(l)
      uptime = Regexp.last_match[0].to_f.round
    end
  end
  uptime
end

.getdumpsys(qualifier) ⇒ Hash

Get the ‘input’ information from dumpsys

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (Hash)

    hash containing input information from dumpsys



51
52
53
54
# File 'lib/device_api/android/adb.rb', line 51

def self.getdumpsys(qualifier)
  lines = dumpsys(qualifier, 'input')
  process_dumpsys('(.*):\s+(.*)', lines)
end

.getphoneinfo(qualifier) ⇒ Hash

Get the ‘iphonesubinfo’ from dumpsys

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (Hash)

    hash containing iphonesubinfo information from dumpsys



59
60
61
62
# File 'lib/device_api/android/adb.rb', line 59

def self.getphoneinfo(qualifier)
  lines = dumpsys(qualifier, 'iphonesubinfo')
  process_dumpsys('(.*) =\s+(.*)', lines)
end

.getpowerinfo(qualifier) ⇒ Hash

Get the ‘power’ information from dumpsys

Parameters:

  • qualifier (String)

    qualifier of device

Returns:

  • (Hash)

    hash containing power information from dumpsys



105
106
107
108
# File 'lib/device_api/android/adb.rb', line 105

def self.getpowerinfo(qualifier)
  lines = dumpsys(qualifier, 'power')
  process_dumpsys('(.*)=(.*)', lines)
end

.getprop(qualifier) ⇒ Hash

Get the properties of a specified device

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (Hash)

    hash containing device properties



40
41
42
43
44
45
46
# File 'lib/device_api/android/adb.rb', line 40

def self.getprop(qualifier)
  result = shell(qualifier, 'getprop')

  lines = result.stdout.encode('UTF-16', 'UTF-8', invalid: :replace, replace: '').encode('UTF-8', 'UTF-16').split("\n")

  process_dumpsys('\[(.*)\]:\s+\[(.*)\]', lines)
end

.hide_package(qualifier, package) ⇒ Object

Blocks a package on KitKat and above Returns boolean

Parameters:

  • qualifier

    qualifier of device

  • package

    to hide



360
361
362
363
# File 'lib/device_api/android/adb.rb', line 360

def self.hide_package(qualifier, package)
  result = pm(qualifier, "hide #{package}")
  result.include?('true')
end

.install_apk(options = {}) ⇒ String

Installs a specified apk to a specific device

Parameters:

  • options (Hash) (defaults to: {})

    the options used for installing an apk

Options Hash (options):

  • :apk (String)

    path to apk to install

  • :qualifier (String)

    qualifier of device

Returns:

  • (String)

    return result from adb install command



134
135
136
137
# File 'lib/device_api/android/adb.rb', line 134

def self.install_apk(options = {})
  options[:action] = :install
  change_apk(options)
end

.keyevent(qualifier, keyevent) ⇒ Object

Sends a key event to the specified device

Parameters:

  • qualifier (String)

    qualifier of device

  • keyevent (String)

    keyevent to send to the device



297
298
299
# File 'lib/device_api/android/adb.rb', line 297

def self.keyevent(qualifier, keyevent)
  shell(qualifier, "input keyevent #{keyevent}").stdout
end

.monkey(qualifier, args) ⇒ Object

Runs monkey testing

Examples:

DeviceAPI::ADB.monkey( qualifier, :package => 'my.lovely.app' )

Parameters:

  • qualifier

    qualifier of device

  • args (Hash)

    hash of arguments used for starting testing

Options Hash (args):

  • :events (String) — default: 10000

    number of events to run

  • :package (String)

    name of package to run the tests against

  • :seed (String)

    pass the seed number (optional)

  • :throttle (String)

    throttle value (optional)



216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/device_api/android/adb.rb', line 216

def self.monkey(qualifier, args)

  events = args[:events] || 10000
  package = args[:package] or raise "package name not provided (:package => 'bbc.iplayer')"
  seed = args[:seed]
  throttle = args[:throttle]

  cmd = "monkey -p #{package} -v #{events}"
  cmd = cmd + " -s #{seed}" if seed
  cmd = cmd + " -t #{throttle}" if throttle

  shell(qualifier, cmd)
end

.pm(qualifier, command) ⇒ Object

Package manager commands

Examples:

DeviceAPI::ADB.pm(qualifier, ‘list packages’)

Parameters:

  • qualifier

    qualifier of device

  • command

    command to issue to the package manager



343
344
345
# File 'lib/device_api/android/adb.rb', line 343

def self.pm(qualifier, command)
  shell(qualifier, "pm #{command}").stdout
end

.process_dumpsys(regex_string, data) ⇒ Hash

Processes the results from dumpsys to format them into a hash

Parameters:

  • regex_string (String)

    regex string used to separate the results from the keys

  • data (Array)

    data returned from dumpsys

Returns:

  • (Hash)

    hash containing the keys and values as distinguished by the supplied regex



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/device_api/android/adb.rb', line 90

def self.process_dumpsys(regex_string, data)
  props = {}
  regex = Regexp.new(regex_string)
  data.each do |line|
    if regex.match(line)
      props[Regexp.last_match[1]] = Regexp.last_match[2]
    end
  end

  props
end

.reboot(qualifier, remote) ⇒ nil

Reboots the specified device Remote devices are rebooted and disconnected from system

Parameters:

  • qualifier

    qualifier of device

Returns:

  • (nil)

    Nil if successful, otherwise an error is raised



193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/device_api/android/adb.rb', line 193

def self.reboot(qualifier, remote)
  if remote
    begin
      system("adb -s #{qualifier} reboot &")
      self.disconnect(qualifier.split(":").first)
    rescue => e
      raise ADBCommandError.new(e)
    end
  else
    result = execute("adb -s #{qualifier} reboot && adb -s #{qualifier} wait-for-device shell 'while [[ $(getprop dev.bootcomplete | tr -d '\r') != 1 ]    ]; do sleep 1; printf .; done'")
    raise ADBCommandError.new(result.stderr) if result.exit != 0
  end
end

.screencap(qualifier, args) ⇒ Object

Take a screenshot from the device

Examples:

DeviceAPI::ADB.screenshot( qualifier, :filename => '/tmp/filename.png' )

Parameters:

  • qualifier

    qualifier of device

  • args (Hash)

    hash of arguments

Options Hash (args):

  • :filename (String)

    name (with full path) required to save the image



236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/device_api/android/adb.rb', line 236

def self.screencap( qualifier, args )
  
  filename = args[:filename] or raise "filename not provided (:filename => '/tmp/myfile.png')"
  
  if getprop(qualifier)['ro.build.version.release'].to_i < 7
    convert_carriage_returns = %q{perl -pe 's/\x0D\x0A/\x0A/g'} 
    cmd = "screencap -p | #{convert_carriage_returns} > #{filename}"
  else
    cmd = "screencap -p > #{filename}"
  end

  shell(qualifier, cmd)
end

.shell(qualifier, command) ⇒ Object

ADB Shell command

Parameters:

  • qualifier (String)

    qualifier of device

  • command (String)

    command to execute



304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/device_api/android/adb.rb', line 304

def self.shell(qualifier, command)
  result = execute("adb -s '#{qualifier}' shell #{command}")
  case result.stderr
  when /^error: device unauthorized./
    raise DeviceAPI::UnauthorizedDevice, result.stderr
  when /^error: device not found/
    raise DeviceAPI::DeviceNotFound, result.stderr
  else
    raise ADBCommandError.new(result.stderr)
  end if result.exit != 0

  result
end

.swipe(qualifier, coords = {x_from: 0, y_from: 0, x_to: 0, y_to: 0 }) ⇒ Object

Sends a swipe command to the specified device

Parameters:

  • qualifier (String)

    qualifier of the device

  • coords (Hash) (defaults to: {x_from: 0, y_from: 0, x_to: 0, y_to: 0 })

    hash of coordinates to swipe from / to

Options Hash (coords):

  • :x_from (String) — default: 0

    Coordinate to start from on the X axis

  • :x_to (String) — default: 0

    Coordinate to end on on the X axis

  • :y_from (String) — default: 0

    Coordinate to start from on the Y axis

  • :y_to (String) — default: 0

    Coordinate to end on on the Y axis



325
326
327
# File 'lib/device_api/android/adb.rb', line 325

def self.swipe(qualifier, coords = {x_from: 0, y_from: 0, x_to: 0, y_to: 0 })
  shell(qualifier, "input swipe #{coords[:x_from]} #{coords[:y_from]} #{coords[:x_to]} #{coords[:y_to]}").stdout
end

.uninstall_apk(options = {}) ⇒ String

Uninstalls a specified package from a specified device

Parameters:

  • options (Hash) (defaults to: {})

    the options used for uninstalling a package

Options Hash (options):

  • :package_name (String)

    package to uninstall

  • :qualifier (String)

    qualifier of device

Returns:

  • (String)

    return result from adb uninstall command



144
145
146
147
# File 'lib/device_api/android/adb.rb', line 144

def self.uninstall_apk(options = {})
  options[:action] = :uninstall
  change_apk(options)
end

.wifi(qualifier) ⇒ Object

Returns wifi status and access point name

Examples:

DeviceAPI::ADB.wifi(qualifier)

Parameters:

  • qualifier

    qualifier of device



288
289
290
291
292
# File 'lib/device_api/android/adb.rb', line 288

def self.wifi(qualifier)
  result = shell(qualifier, 'dumpsys wifi | grep mNetworkInfo')

  {:status => result.stdout.match("state:(.*?),")[1].strip, :access_point => result.stdout.match("extra:(.*?),")[1].strip.gsub(/"/,'')}
end