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

Commit ac22f794 authored by Rohit Goyal's avatar Rohit Goyal
Browse files

Update ActivityStarter to handle the case for starting unarchival for archived app.

Test: atest PackageInstallerArchiveTest
Bug: 302114464
Bug: 302610214
Change-Id: Ib02439d38282050a394a912435b72ebeb77677a5
parent e25a5e69
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import com.android.internal.util.function.pooled.PooledLambda;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.KnownPackages;
import com.android.server.pm.PackageArchiver;
import com.android.server.pm.PackageList;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.dex.DynamicCodeLogger;
@@ -1442,4 +1443,10 @@ public abstract class PackageManagerInternal {
     */
    public abstract void sendPackageDataClearedBroadcast(@NonNull String packageName,
            int uid, int userId, boolean isRestore, boolean isInstantApp);

    /**
     * Returns an instance of {@link PackageArchiver} to be used for archiving related operations.
     */
    @NonNull
    public abstract PackageArchiver getPackageArchiver();
}
+131 −1
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.server.pm;

import static android.app.ActivityManager.START_ABORTED;
import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
import static android.app.ActivityManager.START_PERMISSION_DENIED;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
@@ -34,7 +38,10 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
@@ -58,6 +65,7 @@ import android.graphics.drawable.LayerDrawable;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelableException;
import android.os.Process;
import android.os.SELinux;
@@ -71,6 +79,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.ArchiveState.ArchiveActivityInfo;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -185,6 +194,113 @@ public class PackageArchiver {
                        });
    }

    /**
     * Starts unarchival for the package corresponding to the startActivity intent. Note that this
     * will work only if the caller is the default/Home Launcher or if activity is started via Shell
     * identity.
     */
    @NonNull
    public int requestUnarchiveOnActivityStart(@Nullable Intent intent,
            @Nullable String callerPackageName, int userId, int callingUid) {
        String packageName = getPackageNameFromIntent(intent);
        if (packageName == null) {
            Slog.e(TAG, "packageName cannot be null for unarchival!");
            return START_CLASS_NOT_FOUND;
        }
        if (callerPackageName == null) {
            Slog.e(TAG, "callerPackageName cannot be null for unarchival!");
            return START_CLASS_NOT_FOUND;
        }
        if (!isCallingPackageValid(callerPackageName, callingUid, userId)) {
            // Return early as the calling UID does not match caller package's UID.
            return START_CLASS_NOT_FOUND;
        }
        String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
        if ((currentLauncherPackageName == null || !callerPackageName.equals(
                currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
            // TODO(b/311619990): Remove dependency on SHELL_UID for testing
            Slog.e(TAG, TextUtils.formatSimple(
                    "callerPackageName: %s does not qualify for archival of package: " + "%s!",
                    callerPackageName, packageName));
            return START_PERMISSION_DENIED;
        }
        // TODO(b/302114464): Handle edge cases & also divert to a dialog based on
        //  permissions + compat options
        Slog.i(TAG, TextUtils.formatSimple("Unarchival is starting for: %s", packageName));
        try {
            final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
                @Override
                public void send(int code, Intent intent, String resolvedType,
                        IBinder allowlistToken,
                        IIntentReceiver finishedReceiver, String requiredPermission,
                        Bundle options) {
                    // TODO(b/302114464): Handle intent sender status codes
                }
            };

            requestUnarchive(packageName, callerPackageName,
                    new IntentSender((IIntentSender) mLocalSender), UserHandle.of(userId));
        } catch (Throwable t) {
            Slog.e(TAG, TextUtils.formatSimple(
                    "Unexpected error occurred while unarchiving package %s: %s.", packageName,
                    t.getLocalizedMessage()));
            return START_ABORTED;
        }
        return START_SUCCESS;
    }

    /**
     * Returns true if the componentName targeted by the intent corresponds to that of an archived
     * app.
     */
    public boolean isIntentResolvedToArchivedApp(Intent intent, int userId) {
        String packageName = getPackageNameFromIntent(intent);
        if (packageName == null || intent.getComponent() == null) {
            return false;
        }
        PackageState packageState = mPm.snapshotComputer().getPackageStateInternal(packageName);
        if (packageState == null) {
            return false;
        }
        PackageUserState userState = packageState.getUserStateOrDefault(userId);
        if (!PackageArchiver.isArchived(userState)) {
            return false;
        }
        List<ArchiveState.ArchiveActivityInfo> archiveActivityInfoList =
                userState.getArchiveState().getActivityInfos();
        for (int i = 0; i < archiveActivityInfoList.size(); i++) {
            if (archiveActivityInfoList.get(i)
                    .getOriginalComponentName().equals(intent.getComponent())) {
                return true;
            }
        }
        Slog.e(TAG, TextUtils.formatSimple(
                "Package: %s is archived but component to start main activity"
                        + " cannot be found!", packageName));
        return false;
    }

    @Nullable
    private String getCurrentLauncherPackageName(int userId) {
        ComponentName defaultLauncherComponent = mPm.snapshotComputer().getDefaultHomeActivity(
                userId);
        if (defaultLauncherComponent != null) {
            return defaultLauncherComponent.getPackageName();
        }
        return null;
    }

    private boolean isCallingPackageValid(String callingPackage, int callingUid, int userId) {
        int packageUid;
        packageUid = mPm.snapshotComputer().getPackageUid(callingPackage, 0L, userId);
        if (packageUid != callingUid) {
            Slog.w(TAG, TextUtils.formatSimple("Calling package: %s does not belong to uid: %d",
                    callingPackage, callingUid));
            return false;
        }
        return true;
    }

    /** Creates archived state for the package and user. */
    private CompletableFuture<ArchiveState> createArchiveState(String packageName, int userId)
            throws PackageManager.NameNotFoundException {
@@ -403,7 +519,7 @@ public class PackageArchiver {
        Computer snapshot = mPm.snapshotComputer();
        int userId = userHandle.getIdentifier();
        int binderUid = Binder.getCallingUid();
        if (!PackageManagerServiceUtils.isRootOrShell(binderUid)) {
        if (!PackageManagerServiceUtils.isSystemOrRootOrShell(binderUid)) {
            verifyCaller(snapshot.getPackageUid(callerPackageName, 0, userId), binderUid);
        }
        snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
@@ -780,6 +896,20 @@ public class PackageArchiver {
        return bytesFromBitmap(BitmapFactory.decodeFile(path.toString()));
    }

    @Nullable
    private static String getPackageNameFromIntent(@Nullable Intent intent) {
        if (intent == null) {
            return null;
        }
        if (intent.getPackage() != null) {
            return intent.getPackage();
        }
        if (intent.getComponent() != null) {
            return intent.getComponent().getPackageName();
        }
        return null;
    }

    /**
     * Creates serializable archived activities from existing ArchiveState.
     */
+5 −0
Original line number Diff line number Diff line
@@ -6975,6 +6975,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService
            return mInstallerService.getHistoricalSessions(userId);
        }

        @Override
        public PackageArchiver getPackageArchiver() {
            return mInstallerService.mPackageArchiver;
        }

        @Override
        public void sendPackageRestartedBroadcast(@NonNull String packageName,
                int uid, @Intent.Flags int flags) {
+13 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -128,6 +129,7 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
import com.android.server.pm.PackageArchiver;
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
@@ -958,6 +960,17 @@ class ActivityStarter {
            }
        }

        if (Flags.archiving()) {
            PackageArchiver packageArchiver = mService
                    .getPackageManagerInternalLocked()
                    .getPackageArchiver();
            if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
                return packageArchiver
                        .requestUnarchiveOnActivityStart(
                                intent, callingPackage, mRequest.userId, realCallingUid);
            }
        }

        final int launchFlags = intent.getFlags();
        if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
            // Transfer the result target from the source activity to the new one being started,