I want to run any app (say Settings) after rebooting tablet. Can I use os.system or do I have to use other methods.
import os,time
for i in range(0,3):
os.system("adb reboot")
time.sleep(60)
Yes, you can use os.system to execute ADB commands. If you want to validate the command executed successfully, take a look at the check_output(...) function which is apart of the subprocess library. This code snipet is how I choose to implement the check_output function. For the full code look here.
def _run_command(self, cmd):
"""
Execute an adb command via the subprocess module. If the process exits with
a exit status of zero, the output is encapsulated into a ADBCommandResult and
returned. Otherwise, an ADBExecutionError is thrown.
"""
try:
output = check_output(cmd, stderr=subprocess.STDOUT)
return ADBCommandResult(0,output)
except CalledProcessError as e:
raise ADBProcessError(e.cmd, e.returncode, e.output)
To launch an application you can use the command am start -n yourpackagename/.activityname. To launch the Settings App, run adb shell am start -n com.android.settings/com.android.settings.Settings. This stackoverflow question shows you in detail the options you can use to start the application via a command line intent.
Other tips:
I created an ADB wrapper written in python along with a few other python utilities that may aid in what you are trying to accomplish. For example, instead of calling time.sleep(60) to wait for the reboot, you use adb to poll the status of the property sys.boot_completed and once the property is set the device has finished booting and you can launch any application. Below is a reference implementation you can use.
def wait_boot_complete(self, encryption='off'):
"""
When data at rest encryption is turned on, there needs to be a waiting period
during boot up for the user to enter the DAR password. This function will wait
till the password has been entered and the phone has finished booting up.
OR
Wait for the BOOT_COMPLETED intent to be broadcast by check the system
property 'sys.boot_completed'. A ADBProcessError is thrown if there is an
error communicating with the device.
This method assumes the phone will eventually reach the boot completed state.
A check is needed to see if the output length is zero because the property
is not initialized with a 0 value. It is created once the intent is broadcast.
"""
if encryption is 'on':
decrypted = None
target = 'trigger_restart_framework'
print 'waiting for framework restart'
while decrypted is None:
status = self.adb.adb_shell(self.serial, "getprop vold.decrypt")
if status.output.strip() == 'trigger_restart_framework':
decrypted = 'true'
#Wait for boot to complete. The boot completed intent is broadcast before
#boot is actually completed when encryption is enabled. So 'key' off the
#animation.
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
print 'wait for animation to start'
while status == 'stopped':
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
print 'waiting for animation to finish'
while status == 'running':
status = self.adb.adb_shell(self.serial, "getprop init.svc.bootanim").output.strip()
else:
boot = False
while(not boot):
self.adb.adb_wait_for_device(self.serial)
res = self.adb.adb_shell(self.serial, "getprop sys.boot_completed")
if len(res.output.strip()) != 0 and int(res.output.strip()) is 1:
boot = True
Related
I would like to get an adb command with a response of a code that map to current call state
the call state I mean are those in following link
https://developer.android.com/reference/android/telecom/Call.html#STATE_ACTIVE
those values are more representative and getting those values in command shell upon executing the adb command will be very helpful for me
I have only managed to get them on a log as per following command
adb logcat -d | findstr -i InCallFragment.setCallState
but I couldnot get the state value as a response of any adb command
Any help will be much appreciated
Thanks
for more illustration
please connect a phone to the PC , do a phone call and end it
use the above command to dump the buffer
refer to the state value
You can use adb shell service call telecom [code] command. The codes for getCallState() will be different depending on the Android version:
6.0.1: 26
7.0.0: 27
7.1.0: 27
7.1.2: 27
8.0.0: 29
8.1.0: 29
I have achieved what you want to do by modifying a custom ROM (LineageOS) and adding an android.util.Log line to print every state.
In my case I modified class:
Call
frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java
And what I did is inside getState(...) method, adding this line:
Log.i(myTAG, "getState state->" + mState.name());
With this what I have to do is search for myTAG in adb logcat.
I think otherwise you wont be able to do it...
You can dumpsys telecomm service:
adb shell dumpsys telecom
CallsManager:
mCalls:
[TC#7, ACTIVE, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:***, A, childs(0), has_parent(false), [Capabilities: CAPABILITY_HOLD CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO], [Properties:]]
mCallAudioManager:
All calls:
TC#7
Active dialing, or connecting calls:
TC#7
Ringing calls:
Holding calls:
Foreground call:
[TC#7, ACTIVE, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:***, A, childs(0), has_parent(false), [Capabilities: CAPABILITY_HOLD CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO], [Properties:]]
mTtyManager:
mCurrentTtyMode: 0
mInCallController:
mInCallServices (InCalls registered):
.
.
Call TC#7 [2018-06-05 14:38:41.505](MO - outgoing)
To address: tel:***
14:38:41.508 - CREATED:PCR.oR#DMA
14:38:41.511 - SET_CONNECTING (ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}, [8c3d1caa626a79d75b154221ea94852a62fee7b3], UserHandle{0}):PCR.oR#DMA
14:38:41.847 - AUDIO_ROUTE (Leaving state QuiescentEarpieceRoute):PCR.oR->CAMSM.pM_2001->CARSM.pM_SWITCH_FOCUS#DMA_2_2
14:38:41.847 - AUDIO_ROUTE (Entering state ActiveEarpieceRoute):PCR.oR->CAMSM.pM_2001->CARSM.pM_SWITCH_FOCUS#DMA_2_2
14:38:43.442 - BIND_CS (ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}):NOCBIR.oR#DMU
14:38:43.519 - CS_BOUND (ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}):SBC.oSC#DMY
14:38:43.519 - START_CONNECTION (tel:***):SBC.oSC#DMY
14:38:43.703 - CAPABILITY_CHANGE (Current: [[ sup_hld mut !v2a]], Removed [[]], Added [[ sup_hld mut !v2a]]):CSW.hCCC#DMg
14:38:43.706 - SET_DIALING (successful outgoing call):CSW.hCCC#DMg
14:38:47.560 - SET_ACTIVE (active set explicitly):CSW.sA#DNM
14:38:47.639 - CAPABILITY_CHANGE (Current: [[ hld sup_hld mut !v2a]], Removed [[]], Added [[ hld]]):CSW.sCC#DNY
Timings (average for this call, milliseconds):
bind_cs: 77.00
outgoing_time_to_dialing: 187.00
PROBLEM:
Every time I run this python function in my monkeyrunner.py script, it opens a new background instance of (cmd, adb, and conhost). And so, in my automation script, if I have a loop that uses that 100 times, I'm going to see 100 of each cmd, adb, and conhost running in the background (I know this because I enter "ps" in powershell to get the list of processes.) The purpose of the function, if you're curious, is to look for logcat messages from the USB attached Android tablet, to see when processes are finished, so that the script knows when to command screen touches to move forward with automation testing.
action = "____"
waitTime = 1
def adb(logMessage, action):
start = time.time()
p = subprocess.Popen("adb logcat -v time", shell=True, cwd="C:\Users\<USERNAME>\AppData\Local\Android\sdk\platform-tools", stdout=subprocess.PIPE)
for line in p.stdout:
if logMessage in line:
print("Found message!")
break
pass
else:
continue
QUESTION:
How can I use "subprocess" to open adb WITHOUT opening a new instance each time? Is there a way to close the subprocess in the same function?
2 things.
adb logcat is a blocking call. It doesn't return unless you send it a SIGINT (ctrl +c). So inside a script you have to send it the "-d" flag. If you forget your script will keep waiting.
you can wait for a subprocess to complete with p.wait()
Therefore you can try this
action = "____"
waitTime = 1
def adb(logMessage, action):
start = time.time()
p = subprocess.Popen("adb logcat -d -v time", shell=True, cwd="C:\Users\<USERNAME>\AppData\Local\Android\sdk\platform-tools", stdout=subprocess.PIPE)
#p.wait()
for line in p.stdout:
if logMessage in line:
print("Found message!")
break
pass
else:
continue
This will ensure that every adb session launched to look at logact terminates properly and its output is looked at only after it returns
EDIT: You are absolutely right, p.wait() isn't needed and is actually wrong! because the logcat writes more than 4kb to stdout in yourcase. You could try to use commuinicate instead to help with that
I figured it out. To prevent a new session of adb from opening in the background per each time this function is called, all I had to do is place the "p" variable outside the function.... like this...
p = subprocess.Popen("adb logcat -v time", shell=True, cwd="C:\Users\<USERNAME>\AppData\Local\Android\sdk\platform-tools", stdout=subprocess.PIPE)
action = "____"
waitTime = 1
def adb(logMessage, action):
start = time.time()
for line in p.stdout:
if logMessage in line:
print("Found message!")
break
pass
else:
continue
There is still the issue of a single adb session that opens and does not close after the script is run. So, now, instead of 180 sessions (or more) opening, there is a single one. If I find out how to close the session I will update this ticket.
I want to create an AVD (Android Virtual Device) through command line in python. For that, I need to pass a string n to the stdin. I have tried the following
emulator_create = str(subprocess.check_output([android,'create', 'avd', '-n', emulator_name, '-t', target_id, '-b', abi],stdin=PIPE))
emulator_create.communicate("n")
but it raises the following error
raise CalledProcessError(retcode, cmd, output=output)
subprocess.CalledProcessError: Command '['/home/fahim/Android/Sdk/tools/android', 'create', 'avd', '-n', 'samsung_1', '-t', '5', '-b', 'android-tv/x86']' returned non-zero exit status 1
Process finished with exit code 1
What can I do?
There's something not working with your example. subprocess.check_output() returns the output from the child process you want to execute, not a handle to this process. In other words you get a string object (or maybe a bytes object) which you cannot use to manipulate the child process.
Probably what happens is that your script, using subprocess.check_output(), will execute the child process and wait until it is finished before continuing. But since you are never able to communicate with it, it will finish with a non-zero return value which will raise the subprocess.CalledProcessError
Now, using grep as an example of a command that waits on the standard input to execute something (since I don't have an Android Virtual Device creator installed) you could do this:
#!/usr/bin/env python2.7
import subprocess
external_command = ['/bin/grep', 'StackOverflow']
input_to_send = '''Almost every body uses Facebook
You can also say that about Google
But you can find an answer on StackOverflow
Even if you're an old programmer
'''
child_process = subprocess.Popen(args=external_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
universal_newlines=True)
stdout_from_child, stderr_from_child = child_process.communicate(input_to_send)
print "Output from child process:", stdout_from_child
child_process.wait()
It will print "Output from child process: But you can find an answer on StackOverflow", which is the output from grep.
In this example, I have
Used the class subprocess.Popen to create an handle to the child process
Setting arguments stdin and stdout with the value subprocess.PIPE to enables us to communicate later on with this process.
Used its .communicate() method to send a string to its standard input. In the same step, I retrieved its standard output and standard error output.
Printed the standard output retrieved in the last step (just so to show that it is actually working)
Waited that this child process is finished
In Python 3.5, it's even simpler:
#!/usr/bin/env python3.5
import subprocess
external_command = ['/bin/grep', 'StackOverflow']
input_to_send = '''Almost every body uses Facebook
You can also say that about Google
But you can find an answer on StackOverflow
Even if you're an old programmer
'''
completed_process_result = subprocess.run(args=external_command,
input=input_to_send,
stdout=subprocess.PIPE,
universal_newlines=True)
print("Output from child process:", completed_process_result.stdout)
In this example, I have:
Used the module function subprocess.run() to execute the command.
The input argument is the string we send to the standard input of the child process
The return value is used later on to retreive the output of the child process
Now you have to adapt this code to your situation.
I have code that prints out the output of adb logcat into a wx.TextArea box, this all works great, click the button and logcat prints out, it prints out more when phone taps are made and everything works fine.
toolsDir and pkgName are both strings.
params = [toolsDir + "\\adb.exe", "logcat"]
p = Popen(params, stdout=subprocess.PIPE, bufsize=1)
for line in p.stdout:
self.progressBox.AppendText(line.decode('utf-8'))
However I have adapted this code to only print out the logs that are for a particular app, this is done using the windows 'findstr' function, adb logcat | findstr myApp. The code below initially works, but then stops, and doesn't display anything else whatever happens, button tapped, app closed etc.
Its like the buffer has reached the end and doesn't process any further events.
args = [toolsDir + '\\adb.exe', 'logcat']
args2 = ['findstr', pkgName]
process_adb = subprocess.Popen(args, stdout=subprocess.PIPE, shell=False)
process_fs = subprocess.Popen(args2, stdin=process_adb.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
for line in process_fs.stdout:
self.progressBox.AppendText(line.decode('utf-8'))
How come the bottom code, the one that filters the app name, stops printing out real time logs, but the top doesn't? I'm guessing its something to do with the piping of one command to another.
You can use Python to filter lines:
if pkgName in line:
BTW: it can be problem with two processes because probably second waits for EOF (end of file) or other signal (ie. closed pipe) but its stdin is open all the time waiting for new data from first process..
Check this: python-subprocess-interaction-why-does-my-process-work-with-popen-communicate
I hope to record app(eg.com.clov4r.android.nil) the CPU occupancy when I operate the app(eg.doing monkey test) and finish recording when I eixt the app(eg.finishing monkey test). How to realise it with python?
Some codes:
packagename = 'com.clov4r.android.nil'
cmd1 = 'adb shell top -d 5 | grep com.clov4r.android.nil'
cmd2 = 'adb shell monkey -v -p com.clov4r.android.nil --throttle 500 --ignore-crashes --ignore-timeouts --ignore-security-exceptions --monitor-native-crashes -s 2345 100'
t1 = threading.Thread(target=subprocess.call(cmd1, stdout=open(r'123.txt', 'w')))
t2 = threading.Thread(target=subprocess.call(cmd2))
You can use Thread.join() :
import threading, time
def worker():
time.sleep(5)
t = threading.Thread(target=worker)
t.start()
t.join()
print('finished')
Events are a good way to communicate between threads (http://docs.python.org/2/library/threading.html#event-objects). However, the other problem you will have is that the top command will essentially run forever. I would do something like this:
def run_top(event, top_cmd):
s = subprocess.Popen(top_cmd, stdout=open('123.txt', 'w'))
event.wait() # Wait until event is set, then kill subprocess
s.kill()
def run_monkey(event, monkey_cmd):
subprocess.call(monkey_cmd)
event.set() # Once we're finished set the event to tell the other thread to exit
event = threading.Event()
threading.Thread(target=run_top, args=(event, your_top_command)).start()
threading.Thread(target=run_monkey, args=(event, your_monkey_command)).start()
There might be a way to kill the thread as well but that's pretty ugly, this way is much more controlled.
I would also say run_monkey() doesn't need to be run in a thread, but not sure what other code you have that may require it.