Is possible to programmatically install a dynamically downloaded apk from a custom Android application.
-
I do not know what "dynamic loader, dependent on current user environment" means. The answer supplied by @Lie Ryan shows how you can install an APK downloaded by whatever means you choose. – CommonsWare Jan 05 '11 at 14:23
-
can you see this answer it helps me: https://stackoverflow.com/a/69689612/12536231 – Wadelzubair Oct 23 '21 at 15:43
18 Answers
You can easily launch a market link or an install prompt:
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse("file:///path/to/your.apk"),
"application/vnd.android.package-archive");
startActivity(promptInstall);
Intent goToMarket = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("market://details?id=com.package.name"));
startActivity(goToMarket);
However, you cannot install .apks without user's explicit permission; not unless the device and your program is rooted.

- 239,200
- 50
- 490
- 574

- 62,238
- 13
- 100
- 144
-
38Good answer, but don't hardcode `/sdcard`, since that is wrong on Android 2.2+ and other devices. Use `Environment.getExternalStorageDirectory()` instead. – CommonsWare Jan 05 '11 at 14:22
-
it can only possible for me when m trying to install it form the SD card but when m going to put it on asset folder its give me parsing error !! , may be its not allowed from project directory . – dharmendra Dec 26 '11 at 11:33
-
3The /asset/ directory only exist in your development machine, when the application is compiled to an APK, the /asset/ directory no longer exists since all the assets are zipped inside the APK. If you wish to install from your /asset/ directory, you'll need to extract that into another folder first. – Lie Ryan Dec 26 '11 at 11:42
-
Beware! setType() "automatically clears any data that was previously set (for example by setData(Uri))." http://developer.android.com/reference/android/content/Intent.html#setType%28java.lang.String%29 To be on the safe side, use setDataAndType() instead: http://stackoverflow.com/a/4969421/1097104 – Juuso Ohtonen Jan 04 '13 at 12:40
-
2@LieRyan . Nice to see your answer. I have the rooted device with custom ROM. I want to install themes dynamically without asking user to press install button. Can I do that. – Sharanabasu Angadi Mar 14 '13 at 10:28
-
@cesar will you able to find anything on how to update with explicit permission of the user? The device is rooted. – jeevs Jul 09 '13 at 10:38
-
@Lie Ryan does it work for the paid apps as well? If I run the device rooted – Siddharthan Asokan Sep 18 '13 at 22:49
-
@LieRyan i have a simple question. I downloaded my apk from net and going to update the downloaded apk. Now i would like to know how would i synchronize both, means the progress bar needs to display both the download and upload activity together. Help me in this – AndroidOptimist Mar 04 '14 at 06:23
-
Installing app using .apk will remove it's ownership from Play Store or not ? Referencing to this thread: [Link](http://stackoverflow.com/questions/10620464/reinstall-application-apk-programmatically-without-downloading) – Saurabh Jain Feb 09 '16 at 14:10
-
4This doesn't seem to work anymore when targetSdk is 25. it gives an exception: "android.os.FileUriExposedException: ...apk exposed beyond app through Intent.getData() ...". How come? – android developer Nov 25 '16 at 23:48
-
5@android developer: I can't test this, at the moment, but on targetSdkVersion >= 24, the following applies: https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed so you have to use FileProvider. – Lie Ryan Nov 26 '16 at 10:04
-
@LieRyan Can you please update the answer to include this? I've failed to use this method, not just for opening APK files, but also for sharing symlinked files. – android developer Nov 26 '16 at 17:45
-
You need to save the APK to a **public** directory.For example: **Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)** – Iman Marashi Dec 02 '16 at 15:08
-
1@LieRyan Is there any way to check if the installation is successful or failed? – Jun 07 '18 at 01:25
-
-
1What works for me is added "REQUEST_INSTALL_PACKAGES" permission in AndroidManifest.xml and start an Intent with action "ACTION_INSTALL_PACKAGE" – MrVasilev Sep 04 '19 at 08:39
-
1You may try this. https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/content/InstallApk.java – 1van Apr 07 '20 at 09:23
File file = new File(dir, "App.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
I had the same problem and after several attempts, it worked out for me this way. I don't know why, but setting data and type separately screwed up my intent.

- 17,696
- 11
- 76
- 110

- 786
- 6
- 10
-
I can't upvote this answer enough. For some reason, setting the intent data and MIME type separately causes ActivityNotFoundException in API level 17. – Brent M. Spell Apr 02 '13 at 15:38
-
I just up-voted this too. This form was the only friggin one I could get to work. Still years later.. what's this freakin bug? Hours wasted. I'm using Eclipse(Helios), BTW. – Tam Apr 12 '13 at 02:28
-
11@BrentM.Spell and others: look into the documentation, you will see that whenever you set only data OR type, the other one is automatically voided, e.g.: `setData()` would cause the type parameter to be removed. You MUST use `setDataAndType()` if you want to give values for both. Here: http://developer.android.com/reference/android/content/Intent.html#setData(android.net.Uri) – Bogdan Alexandru Jun 02 '14 at 10:19
The solutions provided to this question are all applicable to targetSdkVersion
s of 23 and below. For Android N, i.e. API level 24, and above, however, they do not work and crash with the following Exception:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
This is due to the fact that starting from Android 24, the Uri
for addressing the downloaded file has changed. For instance, an installation file named appName.apk
stored on the primary external filesystem of the app with package name com.example.test
would be as
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
for API 23
and below, whereas something like
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
for API 24
and above.
More details on this can be found here and I am not going to go through it.
To answer the question for targetSdkVersion
of 24
and above, one has to follow these steps:
Add the following to the AndroidManifest.xml:
<application
android:allowBackup="true"
android:label="@string/app_name">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.authorityStr"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths"/>
</provider>
</application>
2. Add the following paths.xml
file to the xml
folder on res
in src, main:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="pathName"
path="pathValue"/>
</paths>
The pathName
is that shown in the exemplary content uri example above and pathValue
is the actual path on the system.
It would be a good idea to put a "." (without quotes) for pathValue in the above if you do not want to add any extra subdirectory.
Write the following code to install the apk with the name
appName.apk
on the primary external filesystem:File directory = context.getExternalFilesDir(null); File file = new File(directory, fileName); Uri fileUri = Uri.fromFile(file); if (Build.VERSION.SDK_INT >= 24) { fileUri = FileProvider.getUriForFile(context, context.getPackageName(), file); } Intent intent = new Intent(Intent.ACTION_VIEW, fileUri); intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true); intent.setDataAndType(fileUri, "application/vnd.android" + ".package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(intent); activity.finish();
No permission is also necessary when writing to your own app's private directory on the external filesystem.
I have written an AutoUpdate library here in which I have used the above.

- 5,252
- 1
- 42
- 41
-
2I followed this method. However, it says file corrupted when I press install button. Im able to install the same file by transferring through bluetooth. why so? – Mar 30 '17 at 14:17
-
HI When you get file corrupted error, what is the mode of transportation you gave to your app to bring the APK, if you are downloading from server means, check to flush the streams at the copying file from the server? Since installing the APK which was transferred by Bluetooth working means that is the problem I guess. – Sridhar S Mar 09 '18 at 07:00
-
2java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference – Makvin Jan 09 '19 at 05:37
-
Your library could have at last simple readme with simple instructions about usage... ;) – Renetik May 31 '19 at 08:09
-
4Thank you so much, after a week of struggle with this answer I solved my problem! @SridharS, I know it's been a long time ago, as I see, but if you interested, on line 5, you should add `.authorityStr` after `context.getPackageName()` then it should work. – sehrob Jul 17 '19 at 18:50
-
-
I used the `activity.cacheDir` for file and `
` in xml. This way the apk is in app memory. I just added also the permission and it worked great the app self update – Ultimo_m Sep 16 '21 at 15:05
Well, I dug deeper, and found sources of PackageInstaller application from Android Source.
https://github.com/android/platform_packages_apps_packageinstaller
From manifest I found that it require permission:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
And the actual process of installation occurs after confirmation
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
}
startActivity(newIntent);
-
33android.permission.INSTALL_PACKAGES is available only for the system signed apps. So this would not help a lot – Hubert Liberacki Aug 28 '14 at 00:11
-
@alkersan could you explain how did you import PackatUtil? And also how do you put in this lib on the app classpath? – Cassio Seffrin Sep 27 '21 at 20:00
I just want to share the fact that my apk file was saved to my app "Data" directory and that I needed to change the permissions on the apk file to be world readable in order to allow it to be installed that way, otherwise the system was throwing "Parse error: There is a Problem Parsing the Package"; so using solution from @Horaceman that makes:
File file = new File(dir, "App.apk");
file.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);

- 331
- 2
- 2
-
I'm getting the same parser error! I don't know how to resolve it? I set file.setReadable(true, false) but it's not working for me – Jai Apr 10 '18 at 10:28
This can help others a lot!
First:
private static final String APP_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyAppFolderInStorage/";
private void install() {
File file = new File(APP_DIR + fileName);
if (file.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri downloadedApk = FileProvider.getUriForFile(getContext(), "ir.greencode", file);
intent.setDataAndType(downloadedApk, type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.fromFile(file), type);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
getContext().startActivity(intent);
} else {
Toast.makeText(getContext(), "ّFile not found!", Toast.LENGTH_SHORT).show();
}
}
Second: For android 7 and above you should define a provider in manifest like below!
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="ir.greencode"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
Third: Define path.xml in res/xml folder like below! I'm using this path for internal storage if you want to change it to something else there is a few way! You can go to this link: FileProvider
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="your_folder_name" path="MyAppFolderInStorage/"/>
</paths>
Forth: You should add this permission in manifest:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Allows an application to request installing packages. Apps targeting APIs greater than 25 must hold this permission in order to use Intent.ACTION_INSTALL_PACKAGE.
Please make sure the provider authorities are the same!

- 1,386
- 17
- 16
-
4Thanks, the fourth step was what was missing in all of them. – Amin Keshavarzian May 25 '19 at 13:45
-
2Indeed, the 4th point is absolutely necessary. All other answers missed it completely. – whiteagle Aug 26 '19 at 12:05
-
Isn't android.permission.REQUEST_INSTALL_PACKAGES available to only system apps or apps signed with system keystore or whatever? – pavi2410 Nov 05 '19 at 12:52
-
@Pavitra Allows an application to request installing packages. Apps targeting APIs greater than 25 must hold this permission in order to use Intent.ACTION_INSTALL_PACKAGE. Protection level: signature – Hadi Note Nov 06 '19 at 07:28
-
This code does not use Intent.ACTION_INSTALL_PACKAGE. So why this permission?? – Alexander Dyagilev Nov 20 '19 at 10:50
-
@AlexanderDyagilev Google said that not me! They try to do better in latest android versions! If you try it will not work in some android versions, There is a lots of custom and official versions of android you can't tell which one does not need it! – Hadi Note Nov 23 '19 at 13:14
-
1In your third step, the `external-path` `path` and `name` are somewhat ambiguous. If my file is downloaded to `storage/emulated/0/Android/data/com.mycompany.myapp/files` then what should the `path` and `name` values be, exactly? – birgersp May 20 '21 at 19:27
-
@birgersp Take a look at this https://developer.android.com/reference/androidx/core/content/FileProvider Read carefully! – Hadi Note Jun 17 '21 at 14:51
-
-
I get an error "There was a problem parsing the package", when it try to install. Why? – Andrew Efremov Nov 01 '21 at 14:12
In Android Oreo and above version we have to approach different methods to install apk programatically.
private void installApkProgramatically() {
try {
File path = activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File file = new File(path, filename);
Uri uri;
if (file.exists()) {
Intent unKnownSourceIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName())));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!activity.getPackageManager().canRequestPackageInstalls()) {
startActivityForResult(unKnownSourceIntent, Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE);
} else {
Uri fileUri = FileProvider.getUriForFile(activity.getBaseContext(), activity.getApplicationContext().getPackageName() + ".provider", file);
Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.setDataAndType(fileUri, "application/vnd.android" + ".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
alertDialog.dismiss();
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent intent1 = new Intent(Intent.ACTION_INSTALL_PACKAGE);
uri = FileProvider.getUriForFile(activity.getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent1.setDataAndType(uri,
"application/*");
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent1.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(intent1);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
uri = Uri.fromFile(file);
intent.setDataAndType(uri,
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
} else {
Log.i(TAG, " file " + file.getPath() + " does not exist");
}
} catch (Exception e) {
Log.i(TAG, "" + e.getMessage());
}
}
In Oreo and above version we need unknown resource installation permission. so in activity result u have to check the result for the permission
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE:
switch (resultCode) {
case Activity.RESULT_OK:
installApkProgramatically();
break;
case Activity.RESULT_CANCELED:
//unknown resouce installation cancelled
break;
}
break;
}
}

- 523
- 6
- 16
-
I'm getting an "App not installed" dialog when the update finishes, and, obviously, it doesn't actually install the update. – ɯɐɹʞ Jul 30 '20 at 23:43
-
1`App Not install` dialog will come in some scenario. 1. current running app and installation APK has a different signature. 2.Version should be greater than the currently running application. 3. Both apps should be signed or unsigned. one is signed another one is unsigned means this problem will come. – Yabaze Cool Jul 31 '20 at 15:52
-
i was trying to update a build from the studio and not an actual apk. face palm – ɯɐɹʞ Jul 31 '20 at 23:04
Do not forget to request permissions:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
Add in AndroidManifest.xml the provider and permission:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
Create XML file provider res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Use below example code:
public class InstallManagerApk extends AppCompatActivity {
static final String NAME_APK_FILE = "some.apk";
public static final int REQUEST_INSTALL = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// required permission:
// android.Manifest.permission.WRITE_EXTERNAL_STORAGE
// android.Manifest.permission.READ_EXTERNAL_STORAGE
installApk();
}
...
/**
* Install APK File
*/
private void installApk() {
try {
File filePath = Environment.getExternalStorageDirectory();// path to file apk
File file = new File(filePath, LoadManagerApkFile.NAME_APK_FILE);
Uri uri = getApkUri( file.getPath() ); // get Uri for each SDK Android
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( uri );
intent.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK );
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName);
if ( getPackageManager().queryIntentActivities(intent, 0 ) != null ) {// checked on start Activity
startActivityForResult(intent, REQUEST_INSTALL);
} else {
throw new Exception("don`t start Activity.");
}
} catch ( Exception e ) {
Log.i(TAG + ":InstallApk", "Failed installl APK file", e);
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG)
.show();
}
}
/**
* Returns a Uri pointing to the APK to install.
*/
private Uri getApkUri(String path) {
// Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
// Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
// recommended.
boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
String tempFilename = "tmp.apk";
byte[] buffer = new byte[16384];
int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;
try (InputStream is = new FileInputStream(new File(path));
FileOutputStream fout = openFileOutput(tempFilename, fileMode)) {
int n;
while ((n = is.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
Log.i(TAG + ":getApkUri", "Failed to write temporary APK file", e);
}
if (useFileProvider) {
File toInstall = new File(this.getFilesDir(), tempFilename);
return FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, toInstall);
} else {
return Uri.fromFile(getFileStreamPath(tempFilename));
}
}
/**
* Listener event on installation APK file
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_INSTALL) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(this,"Install succeeded!", Toast.LENGTH_SHORT).show();
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this,"Install canceled!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this,"Install Failed!", Toast.LENGTH_SHORT).show();
}
}
}
...
}

- 721
- 9
- 11
Another solution that doesn't not require to hard-code the receiving app and that is therefore safer:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( Uri.fromFile(new File(pathToApk)) );
startActivity(intent);

- 2,147
- 20
- 37
It's worth noting that if you use the DownloadManager
to kick off your download, be sure to save it to an external location e.g. setDestinationInExternalFilesDir(c, null, "<your name here>).apk";
. The intent with a package-archive type doesn't appear to like the content:
scheme used with downloads to an internal location, but does like file:
. (Trying to wrap the internal path into a File object and then getting the path doesn't work either, even though it results in a file:
url, as the app won't parse the apk; looks like it must be external.)
Example:
int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String downloadedPackageUriString = cursor.getString(uriIndex);
File mFile = new File(Uri.parse(downloadedPackageUriString).getPath());
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.fromFile(mFile), "application/vnd.android.package-archive")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(promptInstall);

- 7,228
- 1
- 55
- 65
Came at this some two months ago and supposedly figured it out. Came back today and couldn't make heads or tails of it. Nothing had changed in my setup as far as I am aware, so apparently whatever past me had come up with wasn't robust enough for present me. I finally managed to get something working again, so documenting it here for future me and anyone else who might stand to benefit from yet another attempt.
This attempt represents a direct Xamarin C# translation of the original Android Java Install APK - Session API example. It could probably use some additional work, but it's at least a start. I have it running on an Android 9 device, though I have the project targetting Android 11.
InstallApkSessionApi.cs
namespace LauncherDemo.Droid
{
using System;
using System.IO;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Widget;
[Activity(Label = "InstallApkSessionApi", LaunchMode = LaunchMode.SingleTop)]
public class InstallApkSessionApi : Activity
{
private static readonly string PACKAGE_INSTALLED_ACTION =
"com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED";
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
this.SetContentView(Resource.Layout.install_apk_session_api);
// Watch for button clicks.
Button button = this.FindViewById<Button>(Resource.Id.install);
button.Click += this.Button_Click;
}
private void Button_Click(object sender, EventArgs e)
{
PackageInstaller.Session session = null;
try
{
PackageInstaller packageInstaller = this.PackageManager.PackageInstaller;
PackageInstaller.SessionParams @params = new PackageInstaller.SessionParams(
PackageInstallMode.FullInstall);
int sessionId = packageInstaller.CreateSession(@params);
session = packageInstaller.OpenSession(sessionId);
this.AddApkToInstallSession("HelloActivity.apk", session);
// Create an install status receiver.
Context context = this;
Intent intent = new Intent(context, typeof(InstallApkSessionApi));
intent.SetAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.GetActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.IntentSender;
// Commit the session (this will start the installation workflow).
session.Commit(statusReceiver);
}
catch (IOException ex)
{
throw new InvalidOperationException("Couldn't install package", ex);
}
catch
{
if (session != null)
{
session.Abandon();
}
throw;
}
}
private void AddApkToInstallSession(string assetName, PackageInstaller.Session session)
{
// It's recommended to pass the file size to openWrite(). Otherwise installation may fail
// if the disk is almost full.
using Stream packageInSession = session.OpenWrite("package", 0, -1);
using Stream @is = this.Assets.Open(assetName);
byte[] buffer = new byte[16384];
int n;
while ((n = @is.Read(buffer)) > 0)
{
packageInSession.Write(buffer, 0, n);
}
}
// Note: this Activity must run in singleTop launchMode for it to be able to receive the intent
// in onNewIntent().
protected override void OnNewIntent(Intent intent)
{
Bundle extras = intent.Extras;
if (PACKAGE_INSTALLED_ACTION.Equals(intent.Action))
{
PackageInstallStatus status = (PackageInstallStatus)extras.GetInt(PackageInstaller.ExtraStatus);
string message = extras.GetString(PackageInstaller.ExtraStatusMessage);
switch (status)
{
case PackageInstallStatus.PendingUserAction:
// This test app isn't privileged, so the user has to confirm the install.
Intent confirmIntent = (Intent) extras.Get(Intent.ExtraIntent);
this.StartActivity(confirmIntent);
break;
case PackageInstallStatus.Success:
Toast.MakeText(this, "Install succeeded!", ToastLength.Short).Show();
break;
case PackageInstallStatus.Failure:
case PackageInstallStatus.FailureAborted:
case PackageInstallStatus.FailureBlocked:
case PackageInstallStatus.FailureConflict:
case PackageInstallStatus.FailureIncompatible:
case PackageInstallStatus.FailureInvalid:
case PackageInstallStatus.FailureStorage:
Toast.MakeText(this, "Install failed! " + status + ", " + message,
ToastLength.Short).Show();
break;
default:
Toast.MakeText(this, "Unrecognized status received from installer: " + status,
ToastLength.Short).Show();
break;
}
}
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.launcherdemo" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
<application android:label="LauncherDemo.Android" android:theme="@style/MainTheme" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>
What I particularly like about this approach is that it does not require any special work in the manifest - no broadcast receivers, file providers, etc. Granted, this takes as its source some APK in the app's assets, whereas a more useful system will probably use some given APK path. I imagine that will introduce a certain level of additional complexity. In addition, I never ran into any issues here (at least as far as I could tell) with the Xamarin GC closing streams before Android was done with them. Nor did I have any issues with the APK not being parsed. I made sure to use a signed APK (the one generated by Visual Studio when deploying to the device worked just fine), and again I did not run into any file access permission issues simply due to using an APK from the app's assets in this example.
One thing that some of the other answers here provided was the idea of making the sideloading permission grant more streamlined. The answer by Yabaze Cool provided this feature:
Intent unKnownSourceIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName()))); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!activity.getPackageManager().canRequestPackageInstalls()) { startActivityForResult(unKnownSourceIntent, Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE); ...
When I tested my translation, I uninstalled both the launcher demo and the app it installed. Not providing the check to canRequestPackageInstalls
made it to where I had to manually press an additional Settings button to take me to the same dialog as does the ACTION_MANAGE_UNKNOWN_APP_SOURCES
intent above. So adding this logic can help to somewhat simplify the installation process for the user.

- 2,793
- 7
- 34
- 62
-
1And naturally within minutes of posting this I find my previous working solution, pretty much identical to the above... Forgive me SO, I have hoarded my knowledge, I am chastened... – Bondolin Oct 29 '21 at 20:04
Yes it's possible. But for that you need the phone to install unverified sources. For example, slideMe does that. I think the best thing you can do is to check if the application is present and send an intent for the Android Market. you should use something the url scheme for android Market.
market://details?id=package.name
I don't know exactly how to start the activity but if you start an activity with that kind of url. It should open the android market and give you the choice to install the apps.

- 94,083
- 31
- 258
- 268

- 13,220
- 6
- 67
- 99
-
As I see, this solution is closest to truth :). But it not appropriate for my case. I need dynamic loader, dependent on current user environment, and going to market - not a good solution. But anyway, thank you. – alkersan Jan 05 '11 at 13:35
try this
String filePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
String title = filePath.substring( filePath.lastIndexOf('/')+1, filePath.length() );
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
MainActivity.this.startActivity(intent);
UpdateNode provides an API for Android to install APK packages from inside another App.
You can just define your Update online and integrate the API into your App - that's it.
Currently the API is in Beta state, but you can already do some tests yourself.
Beside that, UpdateNode offers also displaying messages though the system - pretty useful if you want to tell something important to your users.
I am part of the client dev team and am using at least the message functionality for my own Android App.

- 65
- 6
-
there's some problems with the website. I can't get the api_key and i can't continue registration. – infinite_loop_ Jan 05 '15 at 15:49
-
Hi @infinite_loop_ you can find the API key within your user account section: https://www.updatenode.com/profile/view_keys – sarahara Jan 11 '15 at 06:58
Try this - Write on Manifest:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Write the Code:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/Download";// + "app-release.apk";
File file = new File(fileStr, "app-release.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);

- 2,212
- 3
- 18
- 38

- 1
- 3
-
2You don't need that system-only permission to launch the Package Installer activity – Clocker Sep 19 '16 at 16:26
First add the following line to AndroidManifest.xml :
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Then use the following code to install apk:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/MyApp";// + "app-release.apk";
File file = new File(fileStr, "TaghvimShamsi.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);

- 61
- 2
Based on answer @Uroš Podkrižnik.
Installing the application through the APK may differ for various versions of android (API Levels 21-30):
private var uri: Uri? = null
private var manager: DownloadManager? = null
private var file: File? = null
private var request: DownloadManager.Request? = null
private val REQUEST_WRITE_PERMISSION = 786
private val REQUEST_INSTALL_PACKAGE = 1234
private var receiver: BroadcastReceiver? = null
private var installIntent: Intent? = null
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val externalStorageDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
} else {
@Suppress("DEPRECATION")
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
}
val destination = "$externalStorageDir/Application.apk"
uri = Uri.parse("file://$destination")
file = File(destination)
file?.let { if (it.exists()) it.delete() }
request = DownloadManager.Request(Uri.parse("https://path_to_file/application.apk"))
request?.let {
it.setDescription("Update App")
it.setTitle("Application")
it.setDestinationUri(uri)
}
manager = context?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
// for level android api >= 23 needs permission to write to external storage
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(context!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// here you can display the loading diagram
registerReceiver()
} else {
// request for permission to write to external storage
requestPermissions(
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
REQUEST_WRITE_PERMISSION
)
}
} else {
// here you can display the loading diagram
registerReceiver()
}
}
Create and register receiver:
private val onDownloadComplete = object : BroadcastReceiver() {
// install app when apk is loaded
override fun onReceive(ctxt: Context, intent: Intent) {
val mimeType = "application/vnd.android.package-archive"
receiver = this
try {
installIntent = Intent(Intent.ACTION_VIEW)
installIntent?.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
// for android api >= 24 requires FileProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
installIntent?.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val fileProviderURI = FileProvider.getUriForFile(
context!!,
context!!.applicationContext.packageName + ".provider",
file!!)
installIntent?.setDataAndType(fileProviderURI, mimeType)
// for android api >= 26 requires permission to install from APK in settings
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (context!!.applicationContext.packageManager.canRequestPackageInstalls()) {
installFromAPK()
} else goToSecuritySettings()
} else installFromAPK()
} else {
// for android api < 24 used file:// instead content://
// (no need to use FileProvider)
installIntent?.setDataAndType(uri, mimeType)
installFromAPK()
}
} catch (e: Exception) {
// view error message
}
}
}
private fun registerReceiver() {
manager!!.enqueue(request)
context?.registerReceiver(
onDownloadComplete,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
}
private fun installFromAPK() {
try {
startActivity(installIntent)
context?.unregisterReceiver(receiver)
activity?.finish()
} catch (e: Exception) {
// view error message
}
}
// go to settings for get permission install from APK
private fun goToSecuritySettings() {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(
Uri.parse(String.format(
"package:%s",
context!!.applicationContext.packageName
))
)
try {
startActivityForResult(intent, REQUEST_INSTALL_PACKAGE)
} catch (e: Exception) {
// view error message
}
}
Intercept the result of the permission request WRITE_EXTERNAL_STORAGE:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_WRITE_PERMISSION
&& grantResults.isNotEmpty()
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
try {
// here you can display the loading diagram
registerReceiver()
} catch (e: Exception) {
// view error message
}
}
}
Intercept the result of user selection in security settings:
@RequiresApi(api = Build.VERSION_CODES.O)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_INSTALL_PACKAGE
&& resultCode == AppCompatActivity.RESULT_OK) {
if (context!!.applicationContext.packageManager.canRequestPackageInstalls()) {
installFromAPK()
}
} else {
// view error message
}
}
Add to your manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<application...>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
...
</application>
Add provider_paths.xml file to res/xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
For android API level = 30, return from security settings does not work, so used installation via browser:
try {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://path_to_file/application.apk")
startActivity(intent)
activity?.finish()
} catch (e: ActivityNotFoundException) { }

- 2,346
- 3
- 18
- 34