If I do:
adb install myAppRelease-2012-07-24_14-35-14.apk
When I try to reference the actual .apk file after it is installed
PackageManager pm = this.getPackageManager();
for (ApplicationInfo app : pm.getInstalledApplications(PackageManager.GET_META_DATA)) {
Log.d("PackageList", "package: " + app.packageName + ", sourceDir: " + app.sourceDir);
String appName = "myAppRelease"
if(app.packageName.contains("myApp")){
if(app.sourceDir.contains(appName)){
apkVersion = app.sourceDir.substring(app.sourceDir.indexOf(appName), app.sourceDir.indexOf(apk));
}
}
}
What I see is this:
07-24 14:46:40.190: D/PackageList(7421): package: myApp, sourceDir: /data/app/myApp-1.apk
What I expected to see is this:
07-24 14:46:40.190: D/PackageList(7421): package: myApp, sourceDir: /data/app/myAppRelease-2012-07-24_14-35-14.apk
It appears that it uses
android:label="myApp"
android:versionCode="1"
from the manifest file.
My question is, why doesn't it keep the original .apk filename? I am relying on the .apk filename to display version information for my app.
You can't rely on this, and it definitely doesn't use the version code for the number after the '-'. Especially with newer versions, apps can be moved to the SD card, or forward locked (aka, 'app encryption'), so the actual file on disk can be very different from the original file.
Use PackageManager to get version info, that is guaranteed to be correct and up to date. Change your build system to update the version code and maybe display it in an About dialog or similar so it is easy for users to report it.
Related
I am working on automating the process of launching the dependencies for an android project.
One of the dependencies is launching Termux (Installed through F-Droid not Play store as recommended).
I am trying to launch the installed Termux application through another application and add some commands to its ~./bashrc file for the sake of automation.
I know that an installed app can be launched trough another android app (more details are here).
I wonder to know if this is possible for Termux as well? I wonder to know if we can use intent concept to launch Termux from an android app as well? If yes what is the Termux package name?
I tried using "com.termux" as its packagename in my sample code, but it did not work.
In other words, the following line returns null:
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.termux");
Updated:
I can open another installed app (the app that I developed and installed its apk file on tablet) using Intent concept as shown above (by replacing the appropriate package name instead of termux package name).
Note:
I have installed the Termux through F-Drioid not google play store.
New observation:
I confirmed through Package names application the Termux package name is "com.termux" and its activity class name is also "com.termux.app.termuxActivity"
But seems like the "com.termux" is not accessible through package manager. When i try to pass "com.termux" to the following function it returns false.
Any idea or suggestion?
public boolean isPackageExisted(String targetPackage){
enter code here
PackageManager pm=getPackageManager();
try {
PackageInfo info=pm.getPackageInfo(targetPackage,PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return true;
}
Add com.termux to queries element or declare QUERY_ALL_PACKAGES permission in AndroidManifest.xml if targetSdkVersion is 30+. Check Package Visibility or this article for more info. Otherwise, you will will get PackageSetting{...... com.termux/......} BLOCKED errors in logcat.
<manifest
<queries>
<package android:name="com.termux" />
</queries>
</manifest>
Moreover, you can run commands in termux via RUN_COMMAND intent. The termux-shared lib is published on jitpack since v0.116, check Termux Libraries for import instructions.
Moreover, activity name is com.termux.app.TermuxActivity.
I looked for solutions from many places (Test programmatically if in Android 10+ the legacy external storage access is disabled, https://developer.android.com/google/play/expansion-files.html#Downloading, etc.), but don't know the exact cause of the error.
If app is downloaded from Google Play only in case for android 10 devices, it gives this error:
Caused by: java.lang.IllegalArgumentException: Couldn't get OBB info for /storage/emulated/0/Android/obb/packageName/main.package_version.packageName.obb
The program can find the obb file (even on android 10 devices)
File mainFile = new File(Environment.getExternalStorageDirectory() + "/Android/obb/" + packageName + "/" + "main." + packageVersion + "." + packageName + ".obb")
mainFile.getAbsolutpath()
gets the file path too. However, when Storage Manager is used for app that is downloaded from Google Play store
sm.mountObb(mainFile.getAbsolutePath(), null, mainObbStateChangeListener );
it cannot reach obb expansion file and terminates app with above error.
App can be downloaded from Google Play, but works only for android 9 or below devices. If I create apk and install it on android 10 device, the app can reach obb file without any problem.
What do I miss to be able run app on android 10 devices when it is downloaded from Google Play store? Help would be appreciated.
Update:
I found the cause of the error on Android 10 device, though I still don't know the answer:
If the apk version code (the version code in app gradle) matches to the version code in the obb file name (which it should be if I understood well (https://developer.android.com/google/play/expansion-files), then in the obbexpansionmanager class, the line
sm.mountObb(mainFile.getAbsolutePath(), null, mainObbStateChangeListener );
throws this error:
Caused by: java.lang.IllegalArgumentException: Couldn't get OBB info for /storage/emulated/0/Android/obb/packageName/main.package_version.packageName.obb.
If I change the downloaded app's obb file to a different version code (tested with version code 8, 1, 10 when apk's version code was 9) then app starts and above error does not come up. However, I cannot use different version code in the obb file's name other than the apk's version code to be able to download apk with obb from Google Play store. Furthermore, the media files cannot be reached from obb file unless it matches version codes.
If the device is under Android 10 then app works with same version code in apk and in obb expansion file.
A substitute method can be used instead of Environment.getExternalStorageDirectory() which is Context.getObbDir() which will return the full path of the OBB directory meant for the running application, i.e.: /storage/emulated/0/Android/obb/packageName.
This can replace the code part Environment.getExternalStorageDirectory() + "/Android/obb/" + packageName. More details of this method can be found here.
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 have a largely native Android game on my hands which has to employ APK expansion files for the usual reasons. The project targets API level 21, and is 64-bit only.
Downloading the APK expansion works.
Mounting does not work: for an unencrypted obb, I get an error state 20 (AOBB_STATE_ERROR_INTERNAL). For an encrypted one I get a 21 (AOBB_STATE_ERROR_COULD_NOT_MOUNT).
The APK expansion is created with the jobb tool.
The versionCode is matching the one in my AndroidManifest.xml.
The key (when used), is correct.
The obb file obviously exists, and I'm getting the obb mount path by the way of
String packageName = getPackageName(); // we're in the activity
PackageManager pm = getPackageManager();
return Environment.getExternalStorageDirectory().getAbsolutePath() +
EXP_PATH + packageName + "/main." +
pm.getPackageInfo(packageName, 0).versionCode + "." + packageName +
".obb";`.
The result I get is the same both with an NDK implementation and with a Java implementation.
I understand that this was broken for years, but it has been fixed. Is it really? What am I doing wrong?
After hours and hours of digging, and nearly abandoning hope all, I've found success. And it's ridiculous. But it works.
Make sure that the path you're feeding to storageManager.mountObb() (or AStorageManager_mountObb()) has any leading slashes removed. I.e.
if (obbPath != null && obbPath.startsWith('/'))
{
obbPath = obbPath.substring(1);
}
The one that you get from Environment.getExternalStorageDirectory() will have one.
Hope this helps save someone time.
Need some help by retrieving *.apk file name from Android device programmaticaly ? Could any body provide some Android methods doing that or even shell commands run under Android to get the file location , if I know just a part from file name ?
Also if there is a way how to find out where the *.apk file went after downloading from Market or outside Market ?
Thank you in advance.
List<ApplicationInfo> PackageManager.getInstalledApplications() will give you a list of the installed applications, and ApplicationInfo.sourceDir is the path to the .apk file.
PackageManager pm = getPackageManager();
for (ApplicationInfo app : pm.getInstalledApplications(0)) {
Log.d("PackageList", "package: " + app.packageName + ", sourceDir: " + app.sourceDir);
}
Outputs something like this:
package: com.tmobile.themechooser, sourceDir: /system/app/ThemeChooser.apk
package: com.tmobile.thememanager, sourceDir: /system/app/ThemeManager.apk
package: com.touchtype.swiftkey, sourceDir: /data/app/com.touchtype.swiftkey-1.apk
package: com.twitter.android, sourceDir: /data/app/com.twitter.android-2.apk
package: fm.last.android, sourceDir: /data/app/fm.last.android-1.apk
$ adb shell pm list packages -f
This gave me path in form /data/app/xxx.apk:
https://developer.android.com/reference/android/content/Context.html#getPackageResourcePath%28%29
You can also do adb bugreport and look at the output. You'll want to look for <package name="com.app.package" codePath="path/to/the/app.apk"...
The apps that you download go to /data/app so using ls you can get a list of the downloaded apks.