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

Commit 55ffd057 authored by Ivan Chiang's avatar Ivan Chiang
Browse files

[PM] Handle extra intent of the verification failure in PIA V2

The failure case is launching the extra intent that is included in
the failure result received by the installer when the installation
failed because of developer verification. For this case, the session
is already finished so there is no valid SessionInfo. PIA can NOT get
the sessionInfo at this time. Show the developer verification dialog
without app snippet.

FLAG: android.content.pm.verification_service
Bug: 440105585
Test: manual. videos
Change-Id: I4b9447ac87efd53e6b7738f5aba47ccd4627ac29
parent 5a2c8dbb
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
@@ -156,11 +156,13 @@ class InstallRepository(private val context: Context) : EventResultPersister.Eve

        var callingAttributionTag: String? = null

        val isConfirmDeveloperVerificationAction = (Flags.verificationService()
                && PackageInstaller.ACTION_CONFIRM_DEVELOPER_VERIFICATION == intent.action)

        isSessionInstall =
            PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL == intent.action
                || PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action
                || (Flags.verificationService()
                && PackageInstaller.ACTION_CONFIRM_DEVELOPER_VERIFICATION == intent.action)
                || isConfirmDeveloperVerificationAction

        sessionId = if (isSessionInstall)
            intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, SessionInfo.INVALID_ID)
@@ -196,6 +198,22 @@ class InstallRepository(private val context: Context) : EventResultPersister.Eve
            if (sessionId != SessionInfo.INVALID_ID)
                packageInstaller.getSessionInfo(sessionId)
            else null

        // This case is launching the extra intent that is included in the failure result received
        // by the installer when the installation failed because of developer verification.
        // For this case, the session is already finished so there is no valid SessionInfo.
        // Only show the developer verification dialog without app snippet.
        if (isConfirmDeveloperVerificationAction && sessionInfo == null) {
            val failureReason = intent.getIntExtra(
                PackageInstaller.EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON,
                PackageInstaller.DEVELOPER_VERIFICATION_FAILED_REASON_UNKNOWN
            )
            val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
            val packageInfo = generateStubPackageInfo(packageName)
            isAppUpdating = isAppUpdating(packageInfo)
            return InstallVerificationFailure(failureReason, isAppUpdating)
        }

        if (sessionInfo != null) {
            callingAttributionTag = sessionInfo.installerAttributionTag
            if (sessionInfo.originatingUid != Process.INVALID_UID) {
+6 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ sealed class InstallStage(val stageCode: Int) {
        const val STAGE_SUCCESS = 5
        const val STAGE_FAILED = 6
        const val STAGE_VERIFICATION_CONFIRMATION_REQUIRED = 7
        const val STAGE_VERIFICATION_FAILURE = 8
    }
}

@@ -152,3 +153,8 @@ data class InstallAborted(

class InstallVerificationConfirmationRequired :
    InstallStage(STAGE_VERIFICATION_CONFIRMATION_REQUIRED)

class InstallVerificationFailure(
    val failureReason: Int = PackageInstaller.DEVELOPER_VERIFICATION_FAILED_REASON_UNKNOWN,
    val isAppUpdating: Boolean = false,
) : InstallStage(STAGE_VERIFICATION_FAILURE)
+7 −0
Original line number Diff line number Diff line
@@ -184,6 +184,10 @@ class InstallLaunch : FragmentActivity(), InstallActionListener {
                }
            }

            InstallStage.STAGE_VERIFICATION_FAILURE -> {
                showInstallationDialog()
            }

            else -> {
                Log.d(LOG_TAG, "Unimplemented stage: " + installStage.stageCode)
                showDialogInner(null)
@@ -324,6 +328,9 @@ class InstallLaunch : FragmentActivity(), InstallActionListener {
                // Don't finish the activity at this time, it shows App not installed dialog later
                shouldFinish = false
            }
            InstallStage.STAGE_VERIFICATION_FAILURE -> {
                resultCode = RESULT_OK
            }
        }
        setResult(resultCode, null, shouldFinish)
    }
+49 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.packageinstaller.v2.model.InstallInstalling;
import com.android.packageinstaller.v2.model.InstallStage;
import com.android.packageinstaller.v2.model.InstallSuccess;
import com.android.packageinstaller.v2.model.InstallUserActionRequired;
import com.android.packageinstaller.v2.model.InstallVerificationFailure;
import com.android.packageinstaller.v2.ui.InstallActionListener;
import com.android.packageinstaller.v2.ui.UiUtil;
import com.android.packageinstaller.v2.viewmodel.InstallViewModel;
@@ -221,6 +222,9 @@ public class InstallationFragment extends DialogFragment {
            case InstallStage.STAGE_USER_ACTION_REQUIRED -> {
                updateUserActionRequiredUI(mDialog, (InstallUserActionRequired) installStage);
            }
            case InstallStage.STAGE_VERIFICATION_FAILURE -> {
                updateVerificationFailureUI(mDialog, (InstallVerificationFailure) installStage);
            }
        }

        UiUtil.updateButtonBarLayoutIfNeeded(requireContext(), mDialog);
@@ -678,6 +682,51 @@ public class InstallationFragment extends DialogFragment {
        }
    }

    private void updateVerificationFailureUI(Dialog dialog,
            InstallVerificationFailure installStage) {
        mAppSnippet.setVisibility(View.GONE);
        mCustomMessageTextView.setVisibility(View.VISIBLE);
        mIndeterminateProgressBar.setVisibility(View.GONE);
        mProgressBar.setVisibility(View.GONE);
        // Disable clicking outside of the dialog
        this.setCancelable(false);

        final int verificationUserActionNeededReason = installStage.getFailureReason();
        // For failure case, use strings for policy DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
        final int verificationPolicy = DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;

        // Set title and main message
        int titleResId = getVerificationConfirmationTitleResourceId(
                verificationUserActionNeededReason);
        int msgResId = getVerificationConfirmationMessageResourceId(
                verificationUserActionNeededReason, verificationPolicy,
                installStage.isAppUpdating());
        dialog.setTitle(titleResId);
        mCustomMessageTextView.setText(
                Html.fromHtml(getString(msgResId), Html.FROM_HTML_MODE_LEGACY));

        // Set negative button
        Button negativeButton = UiUtil.getAlertDialogNegativeButton(dialog);
        if (negativeButton != null) {
            negativeButton.setVisibility(View.VISIBLE);
            negativeButton.setText(R.string.ok);
            negativeButton.setFilterTouchesWhenObscured(true);
            UiUtil.applyOutlinedButtonStyle(requireContext(), negativeButton);
            negativeButton.setOnClickListener(view -> {
                // Set clickable of the button to false to avoid the user clicks it
                // more than once quickly
                view.setClickable(false);
                mInstallActionListener.onNegativeResponse(installStage.getStageCode());
            });
        }

        // Hide the positive button
        Button positiveButton = UiUtil.getAlertDialogPositiveButton(dialog);
        if (positiveButton != null) {
            positiveButton.setVisibility(View.GONE);
        }
    }

    private void updateVerificationConfirmationUI(Dialog dialog,
            InstallUserActionRequired installStage) {
        mAppSnippet.setVisibility(View.VISIBLE);
+2 −1
Original line number Diff line number Diff line
@@ -67,7 +67,8 @@ class InstallViewModel(application: Application, val repository: InstallReposito

    fun preprocessIntent(intent: Intent, callerInfo: InstallRepository.CallerInfo) {
        val stage = repository.performPreInstallChecks(intent, callerInfo)
        if (stage.stageCode == InstallStage.STAGE_ABORTED) {
        if (stage.stageCode == InstallStage.STAGE_ABORTED
            || stage.stageCode == InstallStage.STAGE_VERIFICATION_FAILURE) {
            _currentInstallStage.value = stage
        } else {
            repository.stageForInstall()
Loading