title: How to programmatically install private .apk?
body: <p>I have managed to download a <code>.apk</code> file I uploaded to an AWS S3 bucket using <code>DownloadManager</code>, but I can't install the package with out user confirmation. I am working in a private dedicated device app that manages the device owner-level stuff. In this case, I have provisioned my device with <code>adb shell set-device-owner</code>.
  I have been looking all over the place and I have figured out some parts of how to achieve the installation.</p>
  <p>I'm working with Android 8.1 and our devices won't be upgraded to a newer Android version.</p>
  <p>My Manifest file has the provider</p>
  <pre><code>&lt;provider
      android:name=&quot;androidx.core.content.FileProvider&quot;
      android:authorities=&quot;${applicationId}.fileprovider&quot;
      android:exported=&quot;false&quot;
      android:grantUriPermissions=&quot;true&quot;&gt;
      &lt;meta-data
          android:name=&quot;android.support.FILE_PROVIDER_PATHS&quot;
          android:resource=&quot;@xml/provider_paths&quot; /&gt;
  &lt;/provider&gt;
  </code></pre>
  <p>as well as the permission
  <code>&lt;uses-permission android:name=&quot;android.permission.REQUEST_INSTALL_PACKAGES&gt;</code></p>
  <p>My <code>provider_paths.xml</code> file is</p>
  <pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
  &lt;paths xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
      &lt;files-path
          name=&quot;files&quot;
          path=&quot;.&quot; /&gt;
  &lt;/paths&gt;
  </code></pre>
  <p>and the code that executes the installation is in a separate class <code>InstallationUtils.java</code> with a public static method <code>installPackage</code> that gets called from a button in the main activity.</p>
  <pre><code>public static boolean installPackage(Context context) {
      File toInstall = new File(context.getFilesDir(), appName+&quot;.apk&quot;);
      
      if (!toInstall.exists()) {
          Log.e(&quot;installPackage&quot;, &quot;File does not exist!&quot;);
          // Handle case
          return false;
      }
  
      if (!toInstall.canRead()) {
          Log.e(&quot;installPackage&quot;, &quot;File can't be read!&quot;);
          // Handle case
          return fasle;
      }
  
  
      Uri apkUri = FileProvider.getUriForFile(context, context.getPackageName() + &quot;.fileprovider&quot;, toInstall);
      Intent intent = new Intent(Intent.ACTION_VIEW);
      intent.setDataAndType(apkUri, &quot;application/vnd.android.package-archive&quot;);
      intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
      context.startActivity(intent);
  </code></pre>
  <p>Some parts of the code like the directories are hard-coded because I am trying to make it work first before using best practices.
  I have also tried using <code>&lt;external-path&gt;</code> and <code>&lt;external-files-path&gt;</code> as well as having a copy of my APK on all these directories (or at least what I believe are the correct directories, I am fairly new to Android development).</p>
  <p>The closest I have gotten to installing the package was being able to open the installer activity, but it says &quot;Unknown&quot; in the action bar, and &quot;Staging app...&quot; in the middle of the Activity. This activity immediately stops with a dialog that says: &quot;There was a problem parsing the package&quot; with only the option to press &quot;OK&quot;, which goes back to the main activity of the DPC app.</p>
  <p>I also saw other alternatives using <code>PackageInstaller</code>, but I don't quite understand them.</p>
  <hr />
  <p>Code for downloading APK from S3 bucket</p>
  <p>The way I downloaded my .apk file was getting a pre-signed URL from AWS S3.</p>
  <p>public static long downloadApkFromS3(
  Context context,
  String fileName,
  String downloadUrl,
  Handler updateHandler) {</p>
  <pre><code>    DownloadManager downloadManager = (DownloadManager) context
              .getSystemService(Context.DOWNLOAD_SERVICE);
  
      Uri uri = Uri.parse(downloadUrl);
      DownloadManager.Request request = new DownloadManager.Request(uri);
  
      request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
      request.setDestinationInExternalPublicDir(getFilesDir().toString(), fileName + &quot;.apk&quot;);
      request.setMimeType(&quot;application/vnd.android.package-archive&quot;);
  
      long downloadId = downloadManager.enqueue(request);
  
      // Run task in background thread to check download progress later
      executor.execute(new Runnable() {
          @Override
          public void run() {
              int progress = 0;
              boolean isDownloadFinished = false;
              while (!isDownloadFinished) {
                  try {
                      Log.i(&quot;Thread&quot;, &quot;Sleeping&quot;);
                      Thread.sleep(1000); // Check every second.
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                 
                  Cursor cursor = downloadManager
                          .query(new DownloadManager.Query().setFilterById(downloadId));
                  if (cursor.moveToFirst()) {
                      int downloadStatus = cursor
                              .getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
                     
                      switch (downloadStatus) {
                          case DownloadManager.STATUS_RUNNING:
                              long totalBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
                              if (totalBytes &gt; 0) {
                                  long downloadedBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                                  progress = (int) (downloadedBytes * 100 / totalBytes);
                              }
                              break;
  
                          case DownloadManager.STATUS_SUCCESSFUL:
                              progress = 100;
                              isDownloadFinished = true;
  
                              // Release executor
                              executor.shutdown();
  
                              // Release handler
                              updateHandler.removeCallbacksAndMessages(null);
                             
  
                              break;
  
                          case DownloadManager.STATUS_PAUSED:
                              break;
                          case DownloadManager.STATUS_PENDING:
                              break;
                          case DownloadManager.STATUS_FAILED:
                              isDownloadFinished = true;
                              break;
                      }
                      Message message = Message.obtain();
                      message.what = UPDATE_DOWNLOAD_PROGRESS;
                      message.arg1 = progress;
  
                      updateHandler.sendMessage(message);
                  }
              }
          }
      });
      return downloadId;
  }
  </code></pre>
  <p>This code gets called from the main activity with with the lines</p>
  <pre><code>Handler updateHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
      @Override
      public boolean handleMessage(@NonNull Message message) {
          if (message.what == UPDATE_DOWNLOAD_PROGRESS) {
              int downloadProgress = message.arg1;                           
              // Update progress bar
              mProgressBar.setProgress(downloadProgress);
              mProgressPercentage.setText(Integer.toString(downloadProgress) + &quot; %&quot;);
          }
          return true;
          }
      });
  InstallationUtils.downloadApkFromUrl(this, &quot;testDownloadFile&quot;, downloadUrl, updateHandler);
  </code></pre>
  <p>where <code>downloadUrl</code> is the presigned URL from the S3 bucket and <code>updateHandler</code> is just for updating the UI when the download is running.</p>