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

Commit 82f7c887 authored by Sumedh Sen's avatar Sumedh Sen
Browse files

Implement Reinstall dialogs in new UI

Bug: 274120822
Test: Follow Pia Manual Test Plan
Flag: android.content.pm.use_pia_v2
Change-Id: I6d26e3f69f14d21c2f3ef9edd4775f85dba41c6f
parent 579a409e
Loading
Loading
Loading
Loading
+39 −17
Original line number Diff line number Diff line
@@ -38,9 +38,10 @@ import android.os.UserManager
import android.text.TextUtils
import android.util.EventLog
import android.util.Log

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.android.packageinstaller.R

import com.android.packageinstaller.common.EventResultPersister
import com.android.packageinstaller.common.EventResultPersister.OutOfIdsException
import com.android.packageinstaller.common.InstallEventReceiver
@@ -52,6 +53,9 @@ import com.android.packageinstaller.v2.model.InstallAborted.Companion.DLG_PACKAG
import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_ANONYMOUS_SOURCE
import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_INSTALL_CONFIRMATION
import com.android.packageinstaller.v2.model.InstallUserActionRequired.Companion.USER_ACTION_REASON_UNKNOWN_SOURCE
import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW
import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_REINSTALL
import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_UPDATE
import com.android.packageinstaller.v2.model.PackageUtil.canPackageQuery
import com.android.packageinstaller.v2.model.PackageUtil.generateStubPackageInfo
import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
@@ -61,13 +65,15 @@ import com.android.packageinstaller.v2.model.PackageUtil.isCallerSessionOwner
import com.android.packageinstaller.v2.model.PackageUtil.isInstallPermissionGrantedOrRequested
import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
import com.android.packageinstaller.v2.model.PackageUtil.localLogv
import java.io.File
import java.io.IOException

import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

import java.io.File
import java.io.IOException

class InstallRepository(private val context: Context) {

    private val packageManager: PackageManager = context.packageManager
@@ -84,6 +90,7 @@ class InstallRepository(private val context: Context) {
    private val _installResult = MutableLiveData<InstallStage>()
    val installResult: LiveData<InstallStage>
        get() = _installResult
    private var installType = INSTALL_TYPE_NEW

    /**
     * Session ID for a session created when caller uses PackageInstaller APIs
@@ -647,12 +654,13 @@ class InstallRepository(private val context: Context) {
                return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
            }
        }
        val isAppUpdating = isAppUpdating(newPackageInfo)
        installType = getInstallType(newPackageInfo)
        val (existingUpdateOwner, requestedUpdateOwner) =
            getUpdateOwners(newPackageInfo, userActionReason, isAppUpdating)
            getUpdateOwners(newPackageInfo, userActionReason,
                /* isAppUpdating= */ (installType != INSTALL_TYPE_NEW))

        return InstallUserActionRequired(USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet,
            isAppUpdating, existingUpdateOwner, requestedUpdateOwner)
            installType, existingUpdateOwner, requestedUpdateOwner)
    }

    /**
@@ -664,12 +672,13 @@ class InstallRepository(private val context: Context) {
    private fun processSessionInfo(sessionInfo: SessionInfo, userActionReason: Int): InstallStage {
        newPackageInfo = generateStubPackageInfo(sessionInfo.getAppPackageName())
        appSnippet = getAppSnippet(context, sessionInfo)
        val isAppUpdating = isAppUpdating(newPackageInfo)
        installType = getInstallType(newPackageInfo)
        val (existingUpdateOwner, requestedUpdateOwner) =
            getUpdateOwners(newPackageInfo, userActionReason, isAppUpdating)
            getUpdateOwners(newPackageInfo, userActionReason,
                /* isAppUpdating= */ (installType != INSTALL_TYPE_NEW))

        return InstallUserActionRequired(USER_ACTION_REASON_INSTALL_CONFIRMATION, appSnippet,
            isAppUpdating, existingUpdateOwner, requestedUpdateOwner)
            installType, existingUpdateOwner, requestedUpdateOwner)
    }

    private fun getUpdateOwners(
@@ -725,9 +734,9 @@ class InstallRepository(private val context: Context) {
        }
    }

    private fun isAppUpdating(newPkgInfo: PackageInfo?): Boolean {
    private fun getInstallType(newPkgInfo: PackageInfo?): Int {
        if (newPkgInfo == null) {
            return false
            return INSTALL_TYPE_NEW
        }
        var pkgName = newPkgInfo.packageName
        // Check if there is already a package on the device with this name
@@ -748,13 +757,25 @@ class InstallRepository(private val context: Context) {
                pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
            )
            // If the package is archived, treat it as an update case.
            if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
                return false
            if (appInfo.isArchived) {
                return INSTALL_TYPE_UPDATE
            } else if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
                return INSTALL_TYPE_NEW
            }

            val currentPkgInfo = packageManager.getPackageInfo(
                pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES)
            val currentVersionCode = currentPkgInfo.longVersionCode
            var newVersionCode = newPkgInfo.longVersionCode

            return if (currentVersionCode == newVersionCode) {
                INSTALL_TYPE_REINSTALL
            } else {
                INSTALL_TYPE_UPDATE
            }
        } catch (e: PackageManager.NameNotFoundException) {
            return false
            return INSTALL_TYPE_NEW
        }
        return true
    }

    /**
@@ -880,7 +901,7 @@ class InstallRepository(private val context: Context) {
        }
        val installId: Int
        try {
            _installResult.value = InstallInstalling(appSnippet)
            _installResult.value = InstallInstalling(appSnippet, installType)
            installId = InstallEventReceiver.addObserver(
                context, EventResultPersister.GENERATE_NEW_ID
            ) { statusCode: Int, legacyStatus: Int, message: String?, serviceId: Int ->
@@ -934,7 +955,8 @@ class InstallRepository(private val context: Context) {
                val intent = packageManager.getLaunchIntentForPackage(newPackageInfo!!.packageName)
                if (isLauncherActivityEnabled(intent)) intent else null
            }
            _installResult.setValue(InstallSuccess(appSnippet, shouldReturnResult, resultIntent))
            _installResult.setValue(
                InstallSuccess(appSnippet, shouldReturnResult, resultIntent, installType))
        } else {
            // TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition
            // statusCode can be STATUS_FAILURE_ABORTED if:
+7 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.Intent
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW

sealed class InstallStage(val stageCode: Int) {

@@ -43,7 +44,7 @@ class InstallReady : InstallStage(STAGE_READY)
data class InstallUserActionRequired(
    val actionReason: Int,
    val appSnippet: PackageUtil.AppSnippet? = null,
    val isAppUpdating: Boolean = false,
    @PackageUtil.InstallType val installType: Int = INSTALL_TYPE_NEW,
    val existingUpdateOwnerLabel: CharSequence? = null,
    val requestedUpdateOwnerLabel: CharSequence? = null,
    val unknownSourcePackageName: String? = null,
@@ -62,7 +63,10 @@ data class InstallUserActionRequired(
    }
}

data class InstallInstalling(val appSnippet: PackageUtil.AppSnippet) :
data class InstallInstalling(
    val appSnippet: PackageUtil.AppSnippet,
    @PackageUtil.InstallType val installType: Int
) :
    InstallStage(STAGE_INSTALLING) {

    val appIcon: Drawable?
@@ -84,6 +88,7 @@ data class InstallSuccess(
     * the newly installed / updated app if a launchable activity exists.
     */
    val resultIntent: Intent? = null,
    @PackageUtil.InstallType val installType: Int,
) : InstallStage(STAGE_SUCCESS) {

    val appIcon: Drawable?
+17 −1
Original line number Diff line number Diff line
@@ -38,11 +38,15 @@ import android.os.Process
import android.os.UserHandle
import android.os.UserManager
import android.util.Log

import androidx.annotation.IntDef

import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.file.Files
import java.nio.file.Path

import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize

@@ -65,9 +69,9 @@ object PackageUtil {
    const val ARGS_INSTALLER_PACKAGE: String = "installer_pkg"
    const val ARGS_IS_ARCHIVE: String = "is_archive"
    const val ARGS_IS_CLONE_USER: String = "clone_user"
    const val ARGS_IS_UPDATING: String = "is_updating"
    const val ARGS_LEGACY_CODE: String = "legacy_code"
    const val ARGS_MESSAGE: String = "message"
    const val ARGS_INSTALL_TYPE: String = "new_app_state"
    const val ARGS_NEW_OWNER: String = "new_owner"
    const val ARGS_PENDING_INTENT: String = "pending_intent"
    const val ARGS_REQUIRED_BYTES: String = "required_bytes"
@@ -78,6 +82,18 @@ object PackageUtil {
    const val ARGS_TITLE: String = "title"
    const val ARGS_UNARCHIVAL_STATUS: String = "unarchival_status"

    @IntDef(
        value = [
            INSTALL_TYPE_NEW,
            INSTALL_TYPE_UPDATE,
            INSTALL_TYPE_REINSTALL,
        ])
    @Retention(AnnotationRetention.SOURCE)
    annotation class InstallType
    const val INSTALL_TYPE_NEW = 0
    const val INSTALL_TYPE_UPDATE = 1
    const val INSTALL_TYPE_REINSTALL = 2

    /**
     * Determines if the UID belongs to the system downloads provider and returns the
     * [ApplicationInfo] of the provider
+3 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.packageinstaller.v2.ui.fragments;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_ACTION_REASON;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_SOURCE_PKG;
import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW;

import android.app.AlertDialog;
import android.app.Dialog;
@@ -144,7 +145,7 @@ public class ExternalSourcesBlockedFragment extends DialogFragment {
        AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
        String sourcePkg = args.getString(ARGS_SOURCE_PKG);

        mDialogData = new InstallUserActionRequired(actionReason, appSnippet, false, null, null,
            sourcePkg);
        mDialogData = new InstallUserActionRequired(actionReason, appSnippet, INSTALL_TYPE_NEW,
            null, null, sourcePkg);
    }
}
+37 −22
Original line number Diff line number Diff line
@@ -19,8 +19,11 @@ package com.android.packageinstaller.v2.ui.fragments;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_ACTION_REASON;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_APP_SNIPPET;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_EXISTING_OWNER;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_IS_UPDATING;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_INSTALL_TYPE;
import static com.android.packageinstaller.v2.model.PackageUtil.ARGS_NEW_OWNER;
import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_NEW;
import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_REINSTALL;
import static com.android.packageinstaller.v2.model.PackageUtil.INSTALL_TYPE_UPDATE;

import android.app.AlertDialog;
import android.app.Dialog;
@@ -71,7 +74,7 @@ public class InstallConfirmationFragment extends DialogFragment {
        Bundle args = new Bundle();
        args.putInt(ARGS_ACTION_REASON, dialogData.getActionReason());
        args.putParcelable(ARGS_APP_SNIPPET, dialogData.getAppSnippet());
        args.putBoolean(ARGS_IS_UPDATING, dialogData.isAppUpdating());
        args.putInt(ARGS_INSTALL_TYPE, dialogData.getInstallType());
        args.putCharSequence(ARGS_EXISTING_OWNER, dialogData.getExistingUpdateOwnerLabel());
        args.putCharSequence(ARGS_NEW_OWNER, dialogData.getRequestedUpdateOwnerLabel());

@@ -101,7 +104,16 @@ public class InstallConfirmationFragment extends DialogFragment {

        int positiveBtnTextRes;
        String title;
        if (mDialogData.isAppUpdating()) {
        switch (mDialogData.getInstallType()) {
            case INSTALL_TYPE_NEW -> {
                title = getString(R.string.title_install);
                positiveBtnTextRes = R.string.button_install;
            }
            case INSTALL_TYPE_REINSTALL -> {
                title = getString(R.string.title_reinstall);
                positiveBtnTextRes = R.string.button_reinstall;
            }
            case INSTALL_TYPE_UPDATE -> {
                if (mDialogData.getExistingUpdateOwnerLabel() != null
                        && mDialogData.getRequestedUpdateOwnerLabel() != null) {
                    title = getString(R.string.title_update_ownership_change,
@@ -112,16 +124,19 @@ public class InstallConfirmationFragment extends DialogFragment {
                    customMessage.setVisibility(View.VISIBLE);
                    String updateOwnerString = getString(R.string.message_update_owner_change,
                        mDialogData.getExistingUpdateOwnerLabel());
                customMessage.setText(Html.fromHtml(updateOwnerString, Html.FROM_HTML_MODE_LEGACY));
                    customMessage.setText(
                            Html.fromHtml(updateOwnerString, Html.FROM_HTML_MODE_LEGACY));
                    customMessage.setMovementMethod(new ScrollingMovementMethod());
                } else {
                    title = getString(R.string.title_update);
                    positiveBtnTextRes = R.string.button_update;
                }
        } else {
            }
            default -> {
                title = getString(R.string.title_install);
                positiveBtnTextRes = R.string.button_install;
            }
        }

        mDialog = new AlertDialog.Builder(requireContext())
            .setTitle(title)
@@ -166,11 +181,11 @@ public class InstallConfirmationFragment extends DialogFragment {
    private void setDialogData(Bundle args) {
        int actionReason = args.getInt(ARGS_ACTION_REASON);
        AppSnippet appSnippet = args.getParcelable(ARGS_APP_SNIPPET, AppSnippet.class);
        boolean isUpdating = args.getBoolean(ARGS_IS_UPDATING);
        int installType = args.getInt(ARGS_INSTALL_TYPE);
        CharSequence existingOwner = args.getCharSequence(ARGS_EXISTING_OWNER);
        CharSequence newOwner = args.getCharSequence(ARGS_NEW_OWNER);

        mDialogData = new InstallUserActionRequired(actionReason, appSnippet, isUpdating,
        mDialogData = new InstallUserActionRequired(actionReason, appSnippet, installType,
            existingOwner, newOwner, null);
    }
}
Loading