Bonjour
I would like to create a simple market applications for android but I face a annoying issue.
The install flow is as below:
1- Download of apk file in the application context :
InputStream input = new BufferedInputStream(url.openStream()) ;
OutputStream output = openFileOutput("xx.apk", Activity.MODE_WORLD_READABLE);
byte data[] = new byte[1024];
while ((count = input.read(data)) != -1 && statusDownload ) {
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
2- When download is finished:
promptInstall = new Intent(Intent.ACTION_VIEW);
File fileApk = new File (getFilesDir() + "/xx.apk");
promptInstall.setDataAndType(Uri.fromFile(fileApk) , Consts.APK_FILE_INSTALL_TYPE);
startActivityForResult(promptInstall,654);
3- After installing (or if cancelled), apk file is deleted:
File fileApk = new File (a.getFilesDir() + "/xx.apk" );
fileApk.delete();
In order to install an apk file, it must be "World Readable", it means that everyone can , after downloading the file (and before installing), get the apk file.
Does anyone know how to set good permissions for avoid external downloads ?
Thank you for reading !
Does anyone know how to set good permissions for avoid external downloads ?
You already have the "good permissions". The installer runs in a separate process from yours, and therefore it must be able to read the file. Since you have no way of granting permissions only to the installer for this, you have to grant read permissions to all possible processes.
For most things, we would use a ContentProvider to get around this limitation. Alas, at least as of a year ago, the installer did not support content:// Uri values.
Related
To be as succinct as possible:
-APK file is not corrupt.
-I can browse to the APK in the phone's file system and manually install it from there without issue.
-I am using the following code to kick off the install process. File location is confirmed correct:
public void installfromlocal()
{
String downloadfilelocation = getsharedresourcestring("updatepackagelocation");
Log.e("installing from",downloadfilelocation);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(downloadfilelocation)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
So far what I could gather from a couple hours on the internet is that apparently I can't make my app install an APK programmatically from external storage. I can also apparently not copy the file to internal storage and install from there.
So what now? Additionally, I get no messages from Logcat. I only get a popup alerting me that there was an error parsing the apk.
I found a solution for me (not so clear why have this issue, but i solve it).
It seems to me that when downloading with DownloadManager you cant access to the downloaded file via URI, and you get access denied (and various file not found exception error) that's why PackageInstaller cannot read at all the manifest (and that's the parse error).
This is what i did, i hope that resolve you problem as well, i know it's not elegant to say the least.
Because of DownloadManager.COLUMN_LOCAL_FILENAME is deprecated i tried with COLUMN_LOCAL_URI to access the file and access its content (q is Cursor)
String strUri = q.getString(q.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
Uri apkUri = Uri.parse(strUri);
with this uri i can access and copy the file to a temp file in getExternalCacheDir()
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(apkUri, "r");
InputStream inFile = new FileInputStream(pfd.getFileDescriptor());
OutputStream outFile = new FileOutputStream(tmpFile);
//copy
byte[] buffer = new byte[1024];
int length;
while ((length = inFile.read(buffer)) > 0) {
outFile.write(buffer, 0, length);
}
outFile.flush();
inFile.close();
outFile.close();
Grab the file created and get its uri (that is accessible) and start the activity with that uri.
I hope it helps
You should use canonical path of the file. From the docs-
A canonical pathname is both absolute and unique. The precise definition of canonical form is system-dependent. This method first converts this pathname to absolute form if necessary, as if by invoking the getAbsolutePath() method, and then maps it to its unique form in a system-dependent way. This typically involves removing redundant names such as "." and ".." from the pathname, resolving symbolic links (on UNIX platforms), and converting drive letters to a standard case (on Microsoft Windows platforms).
I have a superuser access on my device. I used this function very successfully to download and update my application programattically, but since android 6.0 this function stopped working (because of new type of permission requests).
My question is: since I have superuser access on my rooted device, how can edit my function so I can download the external file on the sdcard without asking for permission from user?
here is the function I use to update the app:
public class UpdateAppZ extends AsyncTask<String,Void,Void>{
private Context context;
public void setContext(Context contextf){
context = contextf;
}
#Override
protected Void doInBackground(String... arg0) {
try {
URL url = new URL(arg0[0]);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
String PATH = "/mnt/sdcard/Download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "update.apk");
if(outputFile.exists()){
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File("/mnt/sdcard/Download/update.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
context.startActivity(intent);
} catch (Exception e) {
Log.e("UpdateAPP", "Update error! " + e.getMessage());
}
return null;
}
}
and call:
UpdateAppZ atualizaApp = new UpdateAppZ();
atualizaApp.setContext(getApplicationContext());
atualizaApp.execute("http://85.118.98.251/misho/app-debug.apk");
Download to getExternalFilesDir(). No permission needed while other apps have access.
No you can not do that as from Android 6.0 You need to ask for dangerous permissions at runtime.
Because If the device is running Android 6.0 (API level 23) or higher,
and the app's targetSdkVersion is 23 or higher, the OS will enforce
the app to request permissions from the user at run-time.
Weather you have root access or not.
Source:
https://developer.android.com/guide/topics/security/permissions.html
https://developer.android.com/training/permissions/index.html
https://stackoverflow.com/a/51112708/4650581
You can use getExternalFilesDir if you want to save file without any permission.
As stated in the documentation:
getExternalFilesDir
Added in API level 8
File getExternalFilesDir (String type)
Returns the absolute path to the directory on the primary
shared/external storage device where the application can place
persistent files it owns. These files are internal to the
applications, and not typically visible to the user as media.
This is like getFilesDir() in that these files will be deleted when the
application is uninstalled, however there are some important
differences: Shared storage may not always be available, since
removable media can be ejected by the user. Media state can be checked
using getExternalStorageState(File). There is no security enforced
with these files. For example, any application holding
WRITE_EXTERNAL_STORAGE can write to these files.
If a shared storage device is emulated (as determined by isExternalStorageEmulated(File)),
it's contents are backed by a private user data partition, which means
there is little benefit to storing data here instead of the private
directories returned by getFilesDir(), etc.
Starting in KITKAT, no permissions are required to read or write to the returned path; it's always accessible to the calling app. This only applies to paths generated for package name of the calling application.
To access paths
belonging to other packages, WRITE_EXTERNAL_STORAGE and/or
READ_EXTERNAL_STORAGE are required. On devices with multiple users (as
described by UserManager), each user has their own isolated shared
storage. Applications only have access to the shared storage for the
user they're running as.
The returned path may change over time if
different shared storage media is inserted, so only relative paths
should be persisted.
https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String)
This link may be useful:
Save files on device storage
I need to download some pdf files into data/data/com.**.* folder.
Those files are application specific and only application should read and display it that's the reason storing on data/data/com.**.* folder.
Please let me know how to download into that folder and open/read it in the application.
I know how to download it into SD card, but I do not have idea to downloading to application specific folder.
Please let me know some code examples to do this and also I need to know the capacity/size of the data/data/com.**.* folder.
As long as you want write your own applications Data folder, you can create a FileOutputStream like this FileOutputStream out = new FileOutputStream("/data/data/com.**.*/somefile"); than use that output stream to save file. Using the same way you can create a FileInputStream and read the file after.
You will get Permission Denied if you try to access another application's data folder.
I am not sure for capacity but you can calculate the size of the data folder using this
File dataFolder = new File("/data/data/com.**.*/");
long size = folderSize(dataFolder);
...
public static long folderSize(File directory) {
long length = 0;
for (File file : directory.listFiles()) {
if (file.isFile())
length += file.length();
else
lengthlong += folderSize(file);
}
return length;
}
Hi here i am attaching the link of a tutorial explained.
http://www.mysamplecode.com/2012/06/android-internal-external-storage.html
and there are many discussions going on internet that you should root your phone in order to access the data from data/data folder and I am also attaching some links about the discussion, I hope these are also some of the links that are related to your question
where do i find app data in android
How to access data/data folder in Android device?
and as well as some links that makes out the things without rooting your phone i mean
You can get access to /data/data/com*.* without rooting the device
http://denniskubes.com/2012/09/25/read-android-data-folder-without-rooting/
To Write file
FileOutputStream out = new FileOutputStream("/data/data/your_package_name/file_name.xyz");
To Read file
FileInputStream fIn = new FileInputStream(new File("/data/data/your_package_name/file_name.xyz"));
Now you have your input stream , you can convert it in your file according to the file type .
I am giving you example if your file is contain String data the we can do something like below ,
BufferedReader myReader = new BufferedReader(
new InputStreamReader(fIn));
String mDataRow = "";
String mBuffer = "";
while ((mDataRow = myReader.readLine()) != null) {
mBuffer += mDataRow + "\n";
}
Remember to add write file permission to AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Sorry if this question was asked many times,
But i want to store some files inside my application,,i tried to put them in Raw folder ,,ok it works i can load them when ever i want,,
But can i modify them at run time??
can i download new files and put them in the Raw folder?
I want to do that because if i put them in the SD card...the user can see them without the application, and i don't want that to happen..
Internal Memory is not an option too,,
i aslo used this path..but the files can be seen
directory = new File (Environment.getExternalStorageDirectory().toString() +"/data/"+getPackageName()+"/files");
directory.mkdirs();
if(DeleteFlag!=1 || !downloadTask.isCancelled()){
file1 = new File(directory, audioFileName);
output = new FileOutputStream(file1);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
publishProgress(""+(int)((total*100)/lenghtOfFile));
output.write(data, 0, count);
}
}
But can i modify them at run time?? can i download new files and put them in the Raw folder?
No. Resources are read-only at runtime. Please store downloaded material either in internal storage (e.g., getFilesDir() or external storage (e.g., getExternalFilesDir()).
I want to do that because if i put them in the SD card...the user can see them without the application, and i don't want that to happen..
They can see everything without the application. They can see your raw resources without the application. If they root their device, they can see anything they want without the application. If you have data that you do not want the users to have access to, store it on the Internet and leave it there.
Of course, users tend to prefer apps that do not waste bandwidth and do not try to hide data from them.
The problem is this:
I make an internet connection to some url and receive an HttpResponse with an app_example.apk.
Then I want to create a file (an .apk)
in the sdcard with this data so that this downloaded application
can be installed later.
How can I convert the HttpResponse to an .apk file?
Let's clear some details:
I have to get this apk file through an internet connection to my server
I don't want to install this applications I receive on the sdcard
All of this has to be done in my code, I cannot use android market
I am currently writing to that file.
What I'm doing is converting the HttpResponse to a byte[ ],
then that byte[ ] is written to a file (an .apk) using an ObjectOutputStream.
Like this:
// byte[] appByteArray - already has the internet response converted in bytes
try {
file = new File(Environment.getExternalStorageDirectory()+"/"+appName+".apk");
file.createNewFile();
FileOutputStream stream = null;
stream = new FileOutputStream(file, false);
ObjectOutputStream objectOut =
new ObjectOutputStream(new BufferedOutputStream(stream));
objectOut.writeObject(appByteArray);
objectOut.close();
} catch (Exception e) {
e.printStackTrace();
}
In the end, the file is created
and has the received content.
When I try to install it,
through a VIEW intent (using the default installer)
I get a parse error saying that it could not find the AndroidManifest.xml.
I think that in some step along the way, the received data is being corrupted.
Do you have another method to solve this?
Many thanks
Don't use an ObjectOutputStream, byte array is serialized as Object, not written as raw data.
Are you sure that you have SD card write permission? android.permission.WRITE_EXTERNAL_STORAGE
Don't write into SD card root directory. Number of files in root dir can be limited. Instead create you app subdirectory on SD CARD.
This code works for me:
try {
String filePath = Environment.getExternalStorageDirectory()
+ "/myappdir/" + appName + ".apk";
File file = new File(filePath);
file.getParentFile().mkdirs();
file.createNewFile();
BufferedOutputStream objectOut = new BufferedOutputStream(
new FileOutputStream(file));
objectOut.write(appByteArray);
objectOut.close();
} catch (Exception e) {
e.printStackTrace();
}
This may not be the core problem, but I don't think you want to wrap stream in an ObjectOutputStream, since that is used for object serialization. It could be that it is adding extra data to the file so it can be deserialized with ObjectInputStream.
I would try pulling the apk off of the emulator (or device) and check it's MD5 versus the file on the server to make sure that the bits are being written out correctly.
Take a look at Pavel P's answer.
Also, I would note that your idea of installing the APK using the VIEW intent action does work, as I have tested this technique in the past.
However, unless the user has explicitly gone into Settings → Applications and selected "Allow non-Market applications", your installation will fail and the user will just see a screen telling them that for security reasons the installation has been blocked.
Basically you really need to rely on having fairly tech-savvy users who are willing to overlook a scary security warning and go and disable that setting.