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

Commit 331fa330 authored by Song Chun Fan's avatar Song Chun Fan Committed by Android (Google) Code Review
Browse files

Merge "[PM] Support better transition in PIA V2 (7/N)" into main

parents dda2de47 b147b321
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -270,7 +270,7 @@ public class InstallInstalling extends Activity {
     *                      during an uninstall.
     */
    private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage,
            int serviceId /* ignore */, boolean hasDeveloperVerificationFailure) {
            int serviceId /* ignore */) {
        if (statusCode == PackageInstaller.STATUS_SUCCESS) {
            launchSuccess();
        } else {
@@ -280,7 +280,7 @@ public class InstallInstalling extends Activity {

    /**
     * Send the package to the package installer and then register a event result observer that
     * will call {@link #launchFinishBasedOnResult(int, int, String, int, boolean)}
     * will call {@link #launchFinishBasedOnResult(int, int, String, int)}
     */
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
+2 −3
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ public class UninstallUninstalling extends Activity implements
        } catch (EventResultPersister.OutOfIdsException | IllegalArgumentException e) {
            Log.e(LOG_TAG, "Fails to start uninstall", e);
            onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
                    null, 0, false);
                    null, 0);
        }
    }

@@ -149,8 +149,7 @@ public class UninstallUninstalling extends Activity implements
    }

    @Override
    public void onResult(int status, int legacyStatus, @Nullable String message, int serviceId,
            boolean hasDeveloperVerificationFailure) {
    public void onResult(int status, int legacyStatus, @Nullable String message, int serviceId) {
        if (mCallback != null) {
            // The caller will be informed about the result via a callback
            mCallback.onUninstallComplete(mAppInfo.packageName, legacyStatus, message);
+35 −39
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;

import android.content.Context;
import android.content.Intent;
import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
import android.os.AsyncTask;
import android.util.AtomicFile;
@@ -31,6 +30,8 @@ import android.util.Xml;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.packageinstaller.PackageUtil;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -98,8 +99,15 @@ public class EventResultPersister {
        /**
         * Called when a result is received.
         */
        void onResult(int status, int legacyStatus, @Nullable String message, int serviceId,
                boolean hasDeveloperVerificationFailure);
        void onResult(int status, int legacyStatus, @Nullable String message, int serviceId);

        /**
         * Return true if the intent is handled by the observer. When the intent is handled,
         * do not trigger #onResult() and not remove the observer.
         */
        default boolean onHandleIntent(Intent intent) {
            return false;
        }
    }

    /**
@@ -139,19 +147,6 @@ public class EventResultPersister {
        return parser.getAttributeValue(null, name);
    }

    /**
     * Read a boolean attribute from the current element
     *
     * @param parser The parser to read from
     * @param name The attribute name to read
     *
     * @return The value of the attribute
     */
    private static boolean readBooleanAttribute(@NonNull XmlPullParser parser,
            @NonNull String name) {
        return Boolean.parseBoolean(parser.getAttributeValue(null, name));
    }

    /**
     * Read persisted state.
     *
@@ -176,15 +171,13 @@ public class EventResultPersister {
                    int legacyStatus = readIntAttribute(parser, "legacyStatus");
                    String statusMessage = readStringAttribute(parser, "statusMessage");
                    int serviceId = readIntAttribute(parser, "serviceId");
                    boolean hasDeveloperVerificationFailure = Flags.verificationService()
                            && readBooleanAttribute(parser, "hasDeveloperVerificationFailure");

                    if (mResults.get(id) != null) {
                        throw new Exception("id " + id + " has two results");
                    }

                    mResults.put(id, new EventResult(status, legacyStatus, statusMessage,
                            serviceId, hasDeveloperVerificationFailure));
                            serviceId));
                } else {
                    throw new Exception("unexpected tag");
                }
@@ -198,17 +191,22 @@ public class EventResultPersister {
    }

    /**
     * Add a result. If the result is an pending user action, execute the pending user action
     * directly and do not queue a result.
     * Add a result. If the result is a pending user action, execute the pending user action
     * directly and do not queue a result in version one. In version two, call back the
     * EventResultObserver#onHandleIntent to make sure if the intent is handled first.
     *
     * @param context The context the event was received in
     * @param intent The intent the activity received
     */
    void onEventReceived(@NonNull Context context, @NonNull Intent intent) {
        int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
        Log.d(LOG_TAG, "Received event with status " + status
                + ", action = " + intent.getAction());

        if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
            Intent intentToStart = intent.getParcelableExtra(Intent.EXTRA_INTENT);
        // If it is PIA version one, starts the activity directly.
        if (!PackageUtil.isVersionTwoEnabled(context)
                && status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
            Intent intentToStart = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
            intentToStart.addFlags(FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intentToStart);

@@ -219,27 +217,32 @@ public class EventResultPersister {
        String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
        int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
        int serviceId = intent.getIntExtra(EXTRA_SERVICE_ID, 0);
        boolean hasDeveloperVerificationFailure = Flags.verificationService()
                && intent.hasExtra(PackageInstaller.EXTRA_DEVELOPER_VERIFICATION_FAILURE_REASON);

        EventResultObserver observerToCall = null;
        boolean isIntentHandled = false;
        synchronized (mLock) {
            int numObservers = mObservers.size();
            for (int i = 0; i < numObservers; i++) {
                if (mObservers.keyAt(i) == id) {
                    observerToCall = mObservers.valueAt(i);
                    // If the intent is handled, don't remove the observer, still needs to
                    // receive the later events.
                    isIntentHandled = observerToCall.onHandleIntent(intent);
                    if (!isIntentHandled) {
                        mObservers.removeAt(i);
                    }

                    break;
                }
            }

            if (observerToCall != null) {
                observerToCall.onResult(status, legacyStatus, statusMessage, serviceId,
                        hasDeveloperVerificationFailure);
                // If the intent is handled, don't call back the observer#onResult().
                if (!isIntentHandled) {
                    observerToCall.onResult(status, legacyStatus, statusMessage, serviceId);
                }
            } else {
                mResults.put(id, new EventResult(status, legacyStatus, statusMessage, serviceId,
                        hasDeveloperVerificationFailure));
                mResults.put(id, new EventResult(status, legacyStatus, statusMessage, serviceId));
                writeState();
            }
        }
@@ -291,10 +294,6 @@ public class EventResultPersister {
                                    serializer.attribute(null, "statusMessage",
                                            results.valueAt(i).message);
                                }
                                if (results.valueAt(i).hasDeveloperVerificationFailure) {
                                    serializer.attribute(null, "hasDeveloperVerificationFailure",
                                            "true");
                                }
                                serializer.attribute(null, "serviceId",
                                        Integer.toString(results.valueAt(i).serviceId));
                                serializer.endTag(null, "result");
@@ -351,7 +350,7 @@ public class EventResultPersister {
                EventResult result = mResults.valueAt(resultIndex);

                observer.onResult(result.status, result.legacyStatus, result.message,
                        result.serviceId, result.hasDeveloperVerificationFailure);
                        result.serviceId);
                mResults.removeAt(resultIndex);
                writeState();
            } else {
@@ -382,15 +381,12 @@ public class EventResultPersister {
        public final int legacyStatus;
        @Nullable public final String message;
        public final int serviceId;
        public final boolean hasDeveloperVerificationFailure;

        private EventResult(int status, int legacyStatus, @Nullable String message, int serviceId,
                boolean hasDeveloperVerificationFailure) {
        private EventResult(int status, int legacyStatus, @Nullable String message, int serviceId) {
            this.status = status;
            this.legacyStatus = legacyStatus;
            this.message = message;
            this.serviceId = serviceId;
            this.hasDeveloperVerificationFailure = hasDeveloperVerificationFailure;
        }
    }

+2 −3
Original line number Diff line number Diff line
@@ -298,7 +298,7 @@ public class UninstallAppProgress extends Activity implements
        } catch (EventResultPersister.OutOfIdsException e) {
            Log.e(TAG, "Fails to start uninstall", e);
            onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
                    null, 0, false);
                    null, 0);
        }

        mHandler.sendMessageDelayed(mHandler.obtainMessage(UNINSTALL_IS_SLOW),
@@ -310,8 +310,7 @@ public class UninstallAppProgress extends Activity implements
    }

    @Override
    public void onResult(int status, int legacyStatus, @Nullable String message, int serviceId,
            boolean hasDeveloperVerificationFailure) {
    public void onResult(int status, int legacyStatus, @Nullable String message, int serviceId) {
        Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);
        msg.arg1 = legacyStatus;
        msg.obj = mAppInfo.packageName;
+75 −32
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

@SuppressLint("MissingPermission")
class InstallRepository(private val context: Context) {
class InstallRepository(private val context: Context) : EventResultPersister.EventResultObserver {

    private val packageManager: PackageManager = context.packageManager
    private val packageInstaller: PackageInstaller = packageManager.packageInstaller
@@ -137,6 +137,7 @@ class InstallRepository(private val context: Context) {
     * PackageInfo of the app being installed on device.
     */
    private var newPackageInfo: PackageInfo? = null
    private var wasUserConfirmationTriggeredByPia = false

    /**
     * Extracts information from the incoming install intent, checks caller's permission to install
@@ -887,8 +888,16 @@ class InstallRepository(private val context: Context) {
        packageInstaller.setDeveloperVerificationUserResponse(
            sessionId, DEVELOPER_VERIFICATION_USER_RESPONSE_INSTALL_ANYWAY
        )
        // If it is triggered by PIA itself, show the installing dialog and wait for the
        // result from the receiver. Don't need to set the aborted.
        if (wasUserConfirmationTriggeredByPia) {
            wasUserConfirmationTriggeredByPia = false
            return InstallInstalling(appSnippet, isAppUpdating)
        } else {
            return InstallAborted(
            ABORT_REASON_DONE, activityResultCode = Activity.RESULT_OK)
                ABORT_REASON_DONE, activityResultCode = Activity.RESULT_OK
            )
        }
    }

    /**
@@ -985,9 +994,16 @@ class InstallRepository(private val context: Context) {
            if (localLogv) {
                Log.i(LOG_TAG, "Install permission granted for session $sessionId")
            }
            // If it is triggered by PIA itself, show the installing dialog and wait for the
            // result from the receiver. Don't need to set the aborted.
            if (wasUserConfirmationTriggeredByPia) {
                wasUserConfirmationTriggeredByPia = false
                _installResult.value = InstallInstalling(appSnippet, isAppUpdating)
            } else {
                _installResult.value = InstallAborted(
                    ABORT_REASON_DONE, activityResultCode = Activity.RESULT_OK
                )
            }
            return
        }
        val uri = intent.data
@@ -1016,13 +1032,10 @@ class InstallRepository(private val context: Context) {
        try {
            _installResult.value = InstallInstalling(appSnippet, isAppUpdating)
            installId = InstallEventReceiver.addObserver(
                context, EventResultPersister.GENERATE_NEW_ID
            ) { statusCode: Int, legacyStatus: Int, message: String?, serviceId: Int,
                hasDeveloperVerificationFailure: Boolean ->
                setStageBasedOnResult(statusCode, legacyStatus, message,
                    hasDeveloperVerificationFailure
                context,
                EventResultPersister.GENERATE_NEW_ID,
                this
            )
            }
        } catch (e: OutOfIdsException) {
            setStageBasedOnResult(
                PackageInstaller.STATUS_FAILURE, PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null
@@ -1052,7 +1065,6 @@ class InstallRepository(private val context: Context) {
        statusCode: Int,
        legacyStatus: Int,
        message: String?,
        hasDeveloperVerificationFailure: Boolean = false
    ) {
        if (localLogv) {
            Log.i(
@@ -1071,14 +1083,12 @@ class InstallRepository(private val context: Context) {
                val intent = packageManager.getLaunchIntentForPackage(newPackageInfo!!.packageName)
                if (isLauncherActivityEnabled(intent)) intent else null
            }
            _installResult.setValue(
                InstallSuccess(
            _installResult.value = InstallSuccess(
                appSnippet,
                shouldReturnResult,
                isAppUpdating,
                resultIntent
            )
            )
        } else {
            // TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition
            // statusCode can be STATUS_FAILURE_ABORTED if:
@@ -1093,20 +1103,16 @@ class InstallRepository(private val context: Context) {

            if (shouldReturnResult) {
                val resultIntent = Intent().putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus)
                _installResult.setValue(
                    InstallFailed(
                _installResult.value = InstallFailed(
                    legacyCode = legacyStatus,
                    statusCode = statusCode,
                    shouldReturnResult = true,
                    resultIntent = resultIntent
                )
                )
            } else if (userDenied) {
                _installResult.setValue(InstallAborted(ABORT_REASON_INTERNAL_ERROR))
                _installResult.value = InstallAborted(ABORT_REASON_INTERNAL_ERROR)
            } else {
                _installResult.setValue(
                    InstallFailed(appSnippet, legacyStatus, statusCode, message)
                )
                _installResult.value = InstallFailed(appSnippet, legacyStatus, statusCode, message)
            }
        }
    }
@@ -1140,6 +1146,43 @@ class InstallRepository(private val context: Context) {
    val stagingProgress: LiveData<Int>
        get() = sessionStager?.progress ?: MutableLiveData(0)

    /** Override the callback method of the EventResultPersister.EventResultObserver */
    override fun onResult(
        status: Int,
        legacyStatus: Int,
        message: String?,
        serviceId: Int,
    ) {
        setStageBasedOnResult(status, legacyStatus, message)
    }

    /** Override the callback method of the EventResultPersister.EventResultObserver */
    override fun onHandleIntent(intent: Intent): Boolean {
        val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0)

        // If the status is pending user action, trigger the user confirmation from PIA.
        if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
            val intentToStart = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
            // Get the value of should return result from the original intent and add it into
            // the intentToStart
            val shouldReturnResult = this.intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
            intentToStart!!.putExtra(Intent.EXTRA_RETURN_RESULT, shouldReturnResult)

            // In this case, the caller is PIA itself
            val stage = performPreInstallChecks(
                intentToStart!!,
                CallerInfo(context.packageName, context.applicationInfo.uid))
            if (stage.stageCode == InstallStage.STAGE_ABORTED) {
                _installResult.value = stage
                return false
            }
            wasUserConfirmationTriggeredByPia = true
            stageForInstall()
            return true
        }
        return false
    }

    companion object {
        const val EXTRA_STAGED_SESSION_ID = "com.android.packageinstaller.extra.STAGED_SESSION_ID"
        const val SCHEME_PACKAGE = "package"