title: Android: Download and install update APK without Google Play Store gives apk parse error
body: <p>I have an Android app that, for project requirements, cannot be put in stores. But I want that the restricted number of users of it can update the app when I fix bugs or I intriduce new features.
  To do this, I use the AppUpdater library (<a href="https://github.com/javiersantos/AppUpdater" rel="nofollow noreferrer">https://github.com/javiersantos/AppUpdater</a>), in order to check if a new version of the app is available, and I created a listener for the "update now" button, that triggers a DownloadManager with a ProgressBar, downloading the APK file in the smartphone, and then I use some code I found to try to install it automatically.
  The download works fine, but when the installation code is executed, I receive this error: "Parse Error : There is a problem parsing the package". I already have "debug with USB" mode activated, "Unknown sources" too, and the APK is not corrupt.
  Maybe I provide a wrong file path to the installation code? How to find the path correctly? Any other suggestion? Thanks! This is my code:</p>
  
  <p>Manifest permissions:</p>
  
  <pre><code>&lt;!-- Needs internet to connect to Google Services --&gt;
      &lt;uses-permission android:name="android.permission.INTERNET"/&gt;
      &lt;!-- Permission to check internet connection state --&gt;
      &lt;uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/&gt;
      &lt;!-- Keeps processor from sleeping when a message is received. --&gt;
      &lt;uses-permission android:name="android.permission.WAKE_LOCK"/&gt;
      &lt;!-- Permission to vibrate when receive a notification --&gt;
      &lt;uses-permission android:name="android.permission.VIBRATE"/&gt;
      &lt;!-- Lets app receive data messages. --&gt;
      &lt;uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/&gt;
      &lt;!-- Permission to set alarms again when device is rebooted --&gt;
      &lt;uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/&gt;
      &lt;!-- Permission to disable the keylock --&gt;
      &lt;uses-permission android:name="android.permission.DISABLE_KEYGUARD"/&gt;
      &lt;!-- Permission to write to external storage --&gt;
      &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
      &lt;!-- Permission to read from external storage --&gt;
      &lt;uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /&gt;
  </code></pre>
  
  <p>AppUpdater library code (I put it in the MainActivity's onResume() method):</p>
  
  <pre><code> // Check for app's updates with confirmation dialog
   AppUpdater appUpdater = new AppUpdater(this);
  
   appUpdater.setUpdateFrom(UpdateFrom.JSON)
             .setUpdateJSON(Constants.URL_UPDATE_JSON)
             .setDisplay(Display.DIALOG)
             .setButtonDoNotShowAgain(null)
             .setTitleOnUpdateAvailable("Available update")
             .setContentOnUpdateAvailable("Do you want to update the app?")
             .setButtonUpdate("Update now")
             .setButtonUpdateClickListener(new UpdateButtonClickListener(this))
             .setButtonDismiss("Later")
             .setIcon(R.mipmap.icon_icare)
             .showAppUpdated(false);
  
   appUpdater.start();
  </code></pre>
  
  <p>UpdateButtonClickListener:</p>
  
  <pre><code>public class UpdateButtonClickListener implements DialogInterface.OnClickListener
      {
       Context context;
  
       ProgressDialog progressDialog;
  
       // Constructor
       public UpdateButtonClickListener(Context context)
          {
           this.context = context;
           this.progressDialog = new ProgressDialog(context);
          }
  
  
       @Override
       public void onClick(DialogInterface dialogInterface, int i)
          {
           progressDialog.setMessage("Downloading app");
           progressDialog.setIndeterminate(false);
           progressDialog.setMax(100);
           progressDialog.setProgressNumberFormat("");
           progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
           progressDialog.setCancelable(false);
  
           // Download APK file from server and install it
           DownloadFile downloadFile = new DownloadFile();
           downloadFile.execute();  
          }
  
  
       private class DownloadFile extends AsyncTask&lt;Void, Void, Void&gt;
          {
           @Override
           protected Void doInBackground(Void... voids)
              {
               Uri apk_uri = Uri.parse(Constants.URL_UPDATE_APK);
  
               DownloadManager.Request request = new DownloadManager.Request(apk_uri);
  
               request.setTitle("Downloading app");
               request.setDescription("Please wait while downloading the updated app");
  
               request.setDestinationInExternalFilesDir(context, null, Constants.APK_NAME);
  
               // enqueue this request
               DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
               long downloadID = downloadManager.enqueue(request);
  
               boolean downloading = true;
  
               while(downloading)
                  {
                   DownloadManager.Query q = new DownloadManager.Query();
                   q.setFilterById(downloadID);
  
                   Cursor cursor = downloadManager.query(q);
                   cursor.moveToFirst();
  
                   int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                   int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                   if(cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL)
                      {
                       downloading = false;
                       progressDialog.dismiss();
  
                       // ---- IS THE PATH CORRECT? I WANT THE PATH OF THE FILE JUST DOWNLOADED
                       String localFilePath = cursor.getString(cursor.getColumnIndex("local_uri"));
  
                       File apkFile = new File(localFilePath);
  
                       // ---- THIS IS THE INSTALLATION CODE
                       Intent intent = new Intent(Intent.ACTION_VIEW);
                       intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
                       context.startActivity(intent);
                      }
  
                   final int dl_progress = (int) ((bytes_downloaded * 100) / bytes_total);
  
                   progressDialog.setProgress(dl_progress);
  
                   //Toast.makeText(context, statusMessage(cursor), Toast.LENGTH_LONG).show();
  
                   cursor.close();
                  }
  
               return null;
              }
  
  
           private String statusMessage(Cursor c)
              {
               String msg;
               switch(c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)))
                  {
                   case DownloadManager.STATUS_FAILED:
                          msg = "Download failed!";
                          break;
  
                   case DownloadManager.STATUS_PAUSED:
                          msg = "Download paused!";
                          break;
  
                   case DownloadManager.STATUS_PENDING:
                          msg = "Download pending!";
                          break;
  
                   case DownloadManager.STATUS_RUNNING:
                          msg = "Download in progress!";
                          break;
  
                   case DownloadManager.STATUS_SUCCESSFUL:
                          msg = "Download complete!";
                          break;
  
                   default:
                          msg = "Download is nowhere in sight";
                          break;
                  }
  
               return msg;
              }
  
  
           @Override
           protected void onPreExecute()
              {
               super.onPreExecute();
               progressDialog.show();
              }
  
  
           @Override
           protected void onPostExecute(Void aVoid)
              {    
               super.onPostExecute(aVoid);
              }
          }
      }
  </code></pre>
  
  <p><strong>EDIT:</strong></p>
  
  <p>I'm almost there! As I thought, the error was an incorrect path to APK file. I substituted this line of code:</p>
  
  <pre><code>File apkFile = new File(context.getExternalFilesDir(null), Constants.APK_NAME);
  </code></pre>
  
  <p>Now the installation starts correctly, but after some seconds it closes, as if some crash occurs, and the app doesn't get updated.</p>
  
  <p>This is my Android Monitor log when the app closes while trying to install:</p>
  
  <blockquote>
    <p>05-31 14:01:09.208 22383-28884/? W/System.err:
    java.lang.SecurityException: Permission Denial: reading
    com.android.providers.media.MediaProvider uri
    content://media/external/fs_id from pid=17552, uid=10092 requires
    android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
    05-31 14:01:09.208 22383-28884/? W/System.err:     at
    android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:605)
    05-31 14:01:09.208 22383-28884/? W/System.err:     at
    android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:480)
    05-31 14:01:09.208 22383-28884/? W/System.err:     at
    android.content.ContentProvider$Transport.query(ContentProvider.java:211)
    05-31 14:01:09.208 22383-28884/? W/System.err:     at
    android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112)
    05-31 14:01:09.208 22383-28884/? W/System.err:     at
    android.os.Binder.execTransact(Binder.java:453) 05-31 14:01:09.208
    22383-28884/? E/DatabaseUtils: Writing exception to parcel
                                                      java.lang.SecurityException: Permission Denial: reading
    com.android.providers.media.MediaProvider uri
    content://media/external/fs_id from pid=17552, uid=10092 requires
    android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
                                                          at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:605)
                                                          at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:480)
                                                          at android.content.ContentProvider$Transport.query(ContentProvider.java:211)
                                                          at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112)
                                                          at android.os.Binder.execTransact(Binder.java:453)</p>
  </blockquote>
  
  <p>But my Manifest file already have this permission set, why this behavior?</p>