14

In my app Manifest I have declared the use of the permission:

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

and in my code I check if my app can install from unknown sources:

    public void reinstallApp(Activity activity, String pathname, int request_code)
    {
        if (activity.getPackageManager().canRequestPackageInstalls())
        {
            try
            {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(Uri.fromFile(new File(pathname)), "application/vnd.android.package-archive");
                activity.startActivityForResult(intent, request_code);
            }
            catch (Exception e)
            {
                LogUtilities.show(this, e);
            }
        }
        else
        {
            activity.startActivity(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName()))));
        }
    }

but the "activity.getPackageManager().canRequestPackageInstalls()" is always returning "false", even if I check the allow install from unknow sources in the selection activity.

What's the issue?

The Matrix
  • 1,268
  • 2
  • 14
  • 23

2 Answers2

23

You've to ask for permission first. For that you've to call install permission from unknown sources. I got the answer by just rearranging your code.

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!getPackageManager().canRequestPackageInstalls()) {
                startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", getPackageName()))), 1234);
            } else {
                callInstallProcess();
            }
        } else {
            callInstallProcess();
        }

The above code will be in your onCreate() . The you can verify the result.

@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 1234 && resultCode == Activity.RESULT_OK) {
        if (getPackageManager().canRequestPackageInstalls()) {
            callInstallProcess();
        }
    } else {
        //give the error
    }
}

Where your installation is happening in callInstallProcess();

        try
        {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(new File(pathname)), "application/vnd.android.package-archive");
            activity.startActivityForResult(intent, request_code);
        }
        catch (Exception e)
        {
            LogUtilities.show(this, e);
        }

Don't forget to give permission in AndroidManifest.xml

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  • When I use your callInstallProcess() method I get the exception: `exposed beyond app through Intent.getData()`. I think this is because the .apk file is contained in my application, and somehow the other activity cannot access my files. – Zdravko Donev Jan 07 '19 at 08:12
  • NOTE: if in fragment replace startActivityForResult(...) with getActivity(). startActivityForResult(...) to get the good requestCode in onActivityResult – Duna Oct 21 '19 at 19:01
  • 1
    @ZdravkoDonev FileProvider is what u need. – Ishaan Kumar Jan 25 '20 at 07:13
  • When i call `getPackageManager().canRequestPackageInstalls()` i get `Java.Lang.SecurityException: 'Need to declare android.permission.REQUEST_INSTALL_PACKAGES to call this api'` – Oiproks May 20 '20 at 15:04
5

As stated here, for getPackageManager().canRequestPackageInstalls() to work correctly, you have to declare this permission

    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

in your AndroidManifest.xml, as you already did, and - most likely the cause of your problem - declare the targetSdkVersion in your build.gradle or AndroidManifest.xml to be 26 or higher (in other words, your app has to target Android Oreo or above).

Otherwise this method will always return false, even if the user granted the permission.

Patneu
  • 1,021
  • 1
  • 9
  • 10