I start a shell script using process builder. This has been working fine for days now but today, when I uploaded my files to bitbucket and messed around with them, everything seems to work again except that the ProcessBuilder which can't find the file which is already there:
val processBuilder = ProcessBuilder().command(
filesDir.absolutePath + File.separator + "start.sh").start()
The start.sh script is already in the app's files directory but I'm receiving:
java.io.IOException: Cannot run program "/data/user/0/com.example.project/files/start.sh": error=2, No such file or directory
I've tried to run it like this:
val processBuilder = ProcessBuilder().command("start.sh", filesDir.absolutePath + File.separator).start()
and then I'm receiving access denied although all files have chmod 777 and this file has even a+rx flags.
Well this will not likely help anyone but here is what happened:
I tried to put my code on bitbucket and in the process, I managed to delete everything from my local folder. Luckily my code was still on Bitbucket so I just downloaded a tar file with it and started from scratch. On my second attempt, I managed to do everything correctly but I used the downloaded sources from Bitbucket. Everything was fine except that apparently, when I imported the project directly from Bitbucket, the EOL sequence of the synced/downloaded shell script file changed to CRLF instead of LF... I resaved the file with LF and the problem was solved afterwards.
Related
I have the following lines in my build.gradle file of my Android project:
"./prebuild.sh".execute()
But during the build I get this error:
java.io.IOException: Cannot run program "./prebuild.sh": error=2, No such file or directory
The prebuild.sh script is in the root directory of the app and executable.
What's weird is that this exact build works for everyone on the team, just not on my machine (M1). I also remember that this used to work months ago.
This happens on a fresh clone of the repository and a fresh install of Android Studio.
I think I've narrowed it down to a problem with the working directory. If I try to print it like this:
println new File(".").absolutePath
I get the following:
/Users/ale/.gradle/daemon/6.5/.
Which is obviously not my project directory.
Any hints on what I could do to fix it?
Assuming a functional shell prompt; pass the absolute path instead of . current working directory:
if(rootProject.file('prebuild.sh').exists()) {
commandLine 'sh', rootProject.file('prebuild.sh').absolutePath
} else {
println "missing: prebuild.sh"
}
Or how you start it as process, one can also pass the current working directory as second argument:
def proc = "./prebuild.sh".execute([], rootProject.absolutePath)
proc.waitForProcessOutput(System.out, System.err)
I'd run cd first, then pwd should return the expected value:
def proc = "cd ${rootProject.absolutePath} && pwd".execute()
...
Check if the file has DOS line endings (\r\n). This can lead to a confusing "no such file or directory", because it searches for a file called /bin/sh\r (ending with an actual carriage return), which does not exist.
Background
So far, I was able to install APK files using root (within the app), via this code:
pm install -t -f fullPathToApkFile
and if I want to (try to) install to sd-card :
pm install -t -s fullPathToApkFile
The problem
Recently, not sure from which Android version (issue exists on Android P beta, at least), the above method fails, showing me this message:
avc: denied { read } for scontext=u:r:system_server:s0 tcontext=u:object_r:sdcardfs:s0 tclass=file permissive=0
System server has no access to read file context u:object_r:sdcardfs:s0 (from path /storage/emulated/0/Download/FDroid.apk, context u:r:system_server:s0)
Error: Unable to open file: /storage/emulated/0/Download/FDroid.apk
Consider using a file under /data/local/tmp/
Error: Can't open file: /storage/emulated/0/Download/FDroid.apk
Exception occurred while executing:
java.lang.IllegalArgumentException: Error: Can't open file: /storage/emulated/0/Download/FDroid.apk
at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:306)
at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:884)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:138)
at android.os.ShellCommand.exec(ShellCommand.java:103)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21125)
at android.os.Binder.shellCommand(Binder.java:634)
at android.os.Binder.onTransact(Binder.java:532)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2806)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3841)
at android.os.Binder.execTransact(Binder.java:731)
This seems to also affect popular apps such as "Titanium backup (pro)", which fails to restore apps.
What I've tried
Looking at what's written, it appears it lacks permission to install APK files that are not in /data/local/tmp/.
So I tried the next things, to see if I can overcome it:
set the access to the file (chmod 777) - didn't help.
grant permissions to my app, of both storage and REQUEST_INSTALL_PACKAGES (using ACTION_MANAGE_UNKNOWN_APP_SOURCES Intent) - didn't help.
create a symlink to the file, so that it will be inside the /data/local/tmp/, using official API:
Os.symlink(fullPathToApkFile, symLinkFilePath)
This didn't do anything.
create a symlink using this :
ln -sf $fullPathToApkFile $symLinkFilePath
This partially worked. The file is there, as I can see it in Total Commander app, but when I try to check if it exists there, and when I try to install the APK from there, it fails.
Copy/move (using cp or mv) the file to the /data/local/tmp/ path, and then install from there. This worked, but it has disadvantages: moving is risky because it temporarily hides the original file, and it changes the timestamp of the original file. Copying is bad because of using extra space just for installing (even temporarily) and because it wastes time in doing so.
Copy the APK file, telling it to avoid actual copy (meaning hard link), using this command (taken from here) :
cp -p -r -l $fullPathToApkFile $tempFileParentPath"
This didn't work. It got me this error:
cp: /data/local/tmp/test.apk: Cross-device link
Checking what happens in other cases of installing apps. When you install via via the IDE, it actually does create the APK file in this special path, but if you install via the Play Store, simple APK install (via Intent) or adb (via PC), it doesn't.
Wrote about this here too: https://issuetracker.google.com/issues/80270303
The questions
Is there any way to overcome the disadvantages of installing the APK using root on this special path? Maybe even avoid handling this path at all?
Why does the OS suddenly require to use this path? Why not use the original path instead, just like in the other methods of installing apps? What do the other methods of installing apps do, that somehow avoids using the spacial path?
One solution, in case you don't mind the moving procedure, is to also save&restore the timestamp of the original file, as such:
val tempFileParentPath = "/data/local/tmp/"
val tempFilePath = tempFileParentPath + File(fullPathToApkFile).name
val apkTimestampTempFile = File(context.cacheDir, "apkTimestamp")
apkTimestampTempFile.delete()
apkTimestampTempFile.mkdirs()
apkTimestampTempFile.createNewFile()
root.runCommands("touch -r $fullPathToApkFile ${apkTimestampTempFile.absolutePath}")
root.runCommands("mv $fullPathToApkFile $tempFileParentPath")
root.runCommands("pm install -t -f $tempFilePath")
root.runCommands("mv $tempFilePath $fullPathToApkFile")
root.runCommands("touch -r ${apkTimestampTempFile.absolutePath} $fullPathToApkFile")
apkTimestampTempFile.delete()
It's still a bit dangerous, but better than copying files...
EDIT: Google has shown me a nice workaround for this (here) :
We don't support installation of APKs from random directories on the device. They either need to be installed directly from the host using 'adb install' or you have to stream the contents to install --
$ cat foo.apk | pm install -S APK_SIZE
While I think this is incorrect that they don't support installing of APK files from random paths (always worked before), the workaround does seem to work. All I needed to change in the code of installing an APK file is as such:
val length = File(fullPathToApkFile ).length()
commands.add("cat $fullPathToApkFile | pm install -S $length")
Thing is, now I have some other questions about it :
Does this workaround avoid the moving/copying of the APK into storage, and without affecting the original file ? - seems it does
Will this support any APK file, even large ones? - seems it succeeds in doing it for an APK that takes 433MB, so I think it's safe to use for all sizes.
This is needed only from Android P, right? - so far seems so.
Why does it need the file size as a parameter ? - No idea, but if I remove it, it won't work
Thanks for the answers! I looked everywhere else as well to get a whole setup for OTA to work for Android 10 and so on. It 100% works on Samsung Galaxy Tab 10.1 running Android 10.
Here is a medium article with the code:
https://medium.com/#jnishu1996/over-the-air-ota-updates-for-android-apps-download-apk-silent-apk-installation-auto-launch-8ee6f342197c
The magic is running this command with root access:
process = Runtime.getRuntime().exec("su");
out = process.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
// Get all file permissions
dataOutputStream.writeBytes("chmod 777 " + file.getPath() + "\n");
// Perform silent installation command, all flags are necessary for some reason, only this works reliably post Android 10
String installCommand = "cat " + file.getAbsolutePath() + "| pm install -d -t -S " + file.length();
// Data to send to the LaunchActivity to the app knows it got updated and performs necessary functions to notify backend
// es stands for extraString
// In LaunchActivity onCreate(), you can get this data by running -> if (getIntent().getStringExtra("OTA").equals("true"))
String launchCommandIntentArguments = "--es OTA true --es messageId " + MyApplication.mLastSQSMessage.receiptHandle();
// Start a background thread to wait for 8 seconds before reopening the app's LaunchActivity, and pass necessary arguments
String launchCommand = "(sleep 8; am start -n co.getpresso.Presso/.activities.LaunchActivity " + launchCommandIntentArguments + ")&";
// The entire command is deployed with a ";" in the middle to launchCommand run after installCommand
String installAndLaunchCommand = installCommand + "; " + launchCommand;
// begins the installation
dataOutputStream.writeBytes(installAndLaunchCommand);
dataOutputStream.flush();
// Close the stream operation
dataOutputStream.close();
out.close();
int value = process.waitFor();
I am looking for a solution for copying all the files from a specific directory on the hard drive, to a specific or non specific directory on my android phone, once this device is connected.
I would like these files to be automatically moved (or at least copied) to my phone once I connect it to the computer and run the .py file.
I have windows 7 and python 2.7
I was trying this from another answer but I can't understand because there is few explanation, therefore I cannot get it to work.
edit: I have figured out how to transfer files between to folders but I want to my phone. So how can I fix the error of my system not finding the path of my phone, that'll fix my problem I believe. The code works fine the problem is the path.
Here is my code:
import os
import shutil
sourcePath = r'C:\Users\...\What_to_copy_to_phone'
destPath = r'Computer\XT1032\Internal storage'
for root, dirs, files in os.walk(sourcePath):
#figure out where we're going
dest = destPath + root.replace(sourcePath, '')
#if we're in a directory that doesn't exist in the destination folder
#then create a new folder
if not os.path.isdir(dest):
os.mkdir(dest)
print 'Directory created at: ' + dest
#loop through all files in the directory
for f in files:
#compute current (old) & new file locations
oldLoc = root + '\\' + f
newLoc = dest + '\\' + f
if not os.path.isfile(newLoc):
try:
shutil.copy2(oldLoc, newLoc)
print 'File ' + f + ' copied.'
except IOError:
print 'file "' + f + '" already exists'
I am sorry I am being handful but I thought I had solved it.
In theory, there is no way to access your phone's internal memmory with a drive letter, because, Android connects as an MTP device, and not as a Mass Storage device. But, there are some weird solutions:
Root the phone and get a application which enables "Mass Storage" .
If you can not root and if(only if) both the computer are on the same network, run FTP server in you phone, and you get access for file copy by ftp.
But for you case I recommend adb- adb push C:\src /phone_destination is the best solution.You can google and easily find out way to do this in python.
Google offers adb-sync, which is also available in python. This allows backup/synchronization of files on android device to PC.
The following github repo provides instructions on how to setup the process ie: enable USB Debugging, etc... however I suggest installing 15 second adb installer as opposed to downloading/installing the massive Android SDK just to get adb.
adb-sync: https://github.com/google/adb-sync
15 Sec ADB installer: https://forum.xda-developers.com/showthread.php?t=2588979
A little late to answer, but I use SSH certs and crontab to run a ping command against my local IP and pipe that to an scp recursive copy. It will copy any changes over. No issue yet, and it's been running 4 years straight. I can't for the life of me find the command line that's running.
I want to run a task in gradle which will pull a file placed on Android device sdcard.
Below is the code I am trying.
File copyFile = new File("/storage/emulated/0/abc.png")
project.logger.warn("copy file path:"+copyFile.getAbsolutePath())
if (copyFile.exists()) {
project.logger.warn("file exist:yes")
}else{
project.logger.warn("file exist:no")
}
The problem is the file exists on sdcard, still I get the message in else part "file exist:no".
Can anyone help me to get the file on sdcard?
Gradle runs on your development machine. It not running on your Android device. Your development machine does not have a /storage/emulated/0/abc.png file.
You are welcome to try to have Gradle run an adb pull command to pull the file from the device to your development machine. This Gradle plugin may make this task easier, though I have not tried it.
I have installed QPython in my Android mobile.
I written a statement in the QEdit to read a text file from the below path
/storage/emulated/0/com.hipipal.qpyplus/script3/File1.txt
I used the below statement
fob=open('/storage/emulated/0/com.hipipal.qpyplus/script3/File1.txt','r')
fob.read()
If I run the statement, it is throwing error as:
IOError:[Errno 2] No such file or directory: '/storage/emulated/0/com.hipipal.qpyplus/script3/File1.txt'
1|uo_a116#cancro:/ $
Is the above statement correct?
fob=open('File1.txt','r')
Is not working in version 1.0.4.
fout=open('File2.txt','w')
Was working on version 0.9.6, but is not working in 1.0.4.
The "error" is Read only file system.
It looks like restrictions in the (new 1.0.4) file system library. I post a mail to the editor, but no answer at this time.
For testing, try to write absolute path to your files pointing, for example, to sdcard (/sdcard/out.txt).
I had problems on this versions (>=1.0.4) because launch process of script changes and execution directory is not the same as script directory.
I had to change my scripts to point to absolute paths.
It was tested with qpython developer.
Check this link:
https://github.com/qpython-android/qpython.org/issues/48
You can also try as simple as:
fob=open('File1.txt','r')
fob.read()
Just if the script is in the same folder of the file.
You can change the current working directory to path with script before read file:
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))