Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ccd18fe authored by Sumedh Sen's avatar Sumedh Sen
Browse files

Add ability to cancel staging an APK

APK staging can be cancelled mid-progress

Bug: 182205982
Test: Manual. Try to install a large APK and press cancel on the staging
dialog
Flag: EXEMPT. Bug fix only

Change-Id: I91995f22ad7d7a88d0b9ac1dd4df378541d5aca7
parent 6025b7d0
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ import com.android.packageinstaller.v2.model.PackageUtil.localLogv
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

import java.io.File
@@ -123,6 +124,7 @@ class InstallRepository(private val context: Context) {
    private lateinit var intent: Intent
    private lateinit var appOpRequestInfo: AppOpRequestInfo
    private lateinit var appSnippet: PackageUtil.AppSnippet
    private lateinit var stagingJob: Job

    /**
     * PackageInfo of the app being installed on device.
@@ -346,7 +348,7 @@ class InstallRepository(private val context: Context) {
            }

            sessionStager = SessionStager(context, uri, stagedSessionId)
            GlobalScope.launch(Dispatchers.Main) {
            stagingJob = GlobalScope.launch(Dispatchers.Main) {
                val wasFileStaged = sessionStager!!.execute()

                if (wasFileStaged) {
@@ -1018,6 +1020,12 @@ class InstallRepository(private val context: Context) {
        return maybeDeferUserConfirmation()
    }

    fun abortStaging() {
        sessionStager!!.cancel()
        stagingJob.cancel()
        cleanupStagingSession()
    }

    val stagingProgress: LiveData<Int>
        get() = sessionStager?.progress ?: MutableLiveData(0)

+13 −1
Original line number Diff line number Diff line
@@ -41,7 +41,10 @@ class SessionStager internal constructor(
    val progress: LiveData<Int>
        get() = _progress

    var isRunning = false

    suspend fun execute(): Boolean = withContext(Dispatchers.IO) {
        isRunning = true
        val pi: PackageInstaller = context.packageManager.packageInstaller
        var sessionInfo: PackageInstaller.SessionInfo?
        try {
@@ -60,13 +63,15 @@ class SessionStager internal constructor(
                session.openWrite("PackageInstaller", 0, sizeBytes).use { out ->
                    val buffer = ByteArray(1024 * 1024)
                    while (true) {
                        if (!isRunning) {
                            break
                        }
                        val numRead = instream.read(buffer)
                        if (numRead == -1) {
                            session.fsync(out)
                            break
                        }
                        out.write(buffer, 0, numRead)

                        if (sizeBytes > 0) {
                            totalRead += numRead.toLong()
                            val fraction = totalRead.toFloat() / sizeBytes.toFloat()
@@ -78,6 +83,9 @@ class SessionStager internal constructor(
                sessionInfo = pi.getSessionInfo(stagedSessionId)
            }
        } catch (e: Exception) {
            // Note that when the above while loop is ended prematurely, it may also lead to an
            // IOException with a message: write failed: EPIPE (Broken pipe). However, this is
            // expected
            Log.w(LOG_TAG, "Error staging apk from content URI", e)
            sessionInfo = null
        }
@@ -93,6 +101,10 @@ class SessionStager internal constructor(
        }
    }

    fun cancel() {
        isRunning = false
    }

    private fun getContentSizeBytes(): Long {
        return try {
            context.contentResolver
+3 −2
Original line number Diff line number Diff line
@@ -301,8 +301,9 @@ class InstallLaunch : FragmentActivity(), InstallActionListener {
        if (localLogv) {
            Log.d(LOG_TAG, "Negative button clicked. StageCode: $stageCode")
        }
        if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
            installViewModel!!.cleanupInstall()
        when (stageCode) {
            InstallStage.STAGE_USER_ACTION_REQUIRED -> installViewModel!!.cleanupInstall()
            InstallStage.STAGE_STAGING -> installViewModel!!.abortStaging()
        }
        setResult(RESULT_CANCELED, null, true)
    }
+13 −3
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ package com.android.packageinstaller.v2.ui.fragments;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -29,12 +29,22 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;

import com.android.packageinstaller.R;
import com.android.packageinstaller.v2.model.InstallStage;
import com.android.packageinstaller.v2.ui.InstallActionListener;

public class InstallStagingFragment extends DialogFragment {

    private static final String LOG_TAG = InstallStagingFragment.class.getSimpleName();
    private ProgressBar mProgressBar;
    private AlertDialog mDialog;
    @NonNull
    private InstallActionListener mInstallActionListener;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        mInstallActionListener = (InstallActionListener) context;
    }

    @NonNull
    @Override
@@ -47,7 +57,8 @@ public class InstallStagingFragment extends DialogFragment {
        mDialog = new AlertDialog.Builder(requireContext())
            .setTitle(R.string.title_install_staging)
            .setView(dialogView)
            .setNegativeButton(R.string.button_cancel, null)
            .setNegativeButton(R.string.button_cancel, (dialog, which) ->
                mInstallActionListener.onNegativeResponse(InstallStage.STAGE_STAGING))
            .setCancelable(false)
            .create();

@@ -58,7 +69,6 @@ public class InstallStagingFragment extends DialogFragment {
    @Override
    public void onStart() {
        super.onStart();
        mDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setEnabled(false);
        mProgressBar = mDialog.requireViewById(R.id.progress_bar);
        mProgressBar.setProgress(0);
        mProgressBar.setMax(100);
+4 −0
Original line number Diff line number Diff line
@@ -104,6 +104,10 @@ class InstallViewModel(application: Application, val repository: InstallReposito
        repository.initiateInstall()
    }

    fun abortStaging() {
        repository.abortStaging()
    }

    val stagedSessionId: Int
        get() = repository.stagedSessionId
}