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

Commit ef6e3a2c authored by Samiul Islam's avatar Samiul Islam Committed by Android (Google) Code Review
Browse files

Merge "Add multi-user support for Dependency Installer Service" into main

parents 0d983390 160cdc8e
Loading
Loading
Loading
Loading
+69 −51
Original line number Original line Diff line number Diff line
@@ -32,11 +32,13 @@ import android.content.pm.dependencyinstaller.DependencyInstallerCallback;
import android.content.pm.dependencyinstaller.IDependencyInstallerCallback;
import android.content.pm.dependencyinstaller.IDependencyInstallerCallback;
import android.content.pm.dependencyinstaller.IDependencyInstallerService;
import android.content.pm.dependencyinstaller.IDependencyInstallerService;
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.PackageLite;
import android.os.Binder;
import android.os.Handler;
import android.os.Handler;
import android.os.OutcomeReceiver;
import android.os.OutcomeReceiver;
import android.os.Process;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Slog;
import android.util.Slog;


@@ -63,12 +65,11 @@ public class InstallDependencyHelper {
    private final Context mContext;
    private final Context mContext;
    private final SharedLibrariesImpl mSharedLibraries;
    private final SharedLibrariesImpl mSharedLibraries;
    private final PackageInstallerService mPackageInstallerService;
    private final PackageInstallerService mPackageInstallerService;
    private final Object mRemoteServiceLock = new Object();
    @GuardedBy("mTrackers")
    @GuardedBy("mTrackers")
    private final List<DependencyInstallTracker> mTrackers = new ArrayList<>();
    private final List<DependencyInstallTracker> mTrackers = new ArrayList<>();

    @GuardedBy("mRemoteServices")
    @GuardedBy("mRemoteServiceLock")
    private final ArrayMap<Integer, ServiceConnector<IDependencyInstallerService>> mRemoteServices =
    private ServiceConnector<IDependencyInstallerService> mRemoteService = null;
            new ArrayMap<>();


    InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries,
    InstallDependencyHelper(Context context, SharedLibrariesImpl sharedLibraries,
            PackageInstallerService packageInstallerService) {
            PackageInstallerService packageInstallerService) {
@@ -97,13 +98,18 @@ public class InstallDependencyHelper {


        if (missing.isEmpty()) {
        if (missing.isEmpty()) {
            if (DEBUG) {
            if (DEBUG) {
                Slog.d(TAG, "No missing dependency for " + pkg);
                Slog.d(TAG, "No missing dependency for " + pkg.getPackageName());
            }
            }
            // No need for dependency resolution. Move to installation directly.
            // No need for dependency resolution. Move to installation directly.
            callback.onResult(null);
            callback.onResult(null);
            return;
            return;
        }
        }


        if (DEBUG) {
            Slog.d(TAG, "Missing dependencies found for pkg: " + pkg.getPackageName()
                    + " user: " + userId);
        }

        if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) {
        if (!bindToDependencyInstallerIfNeeded(userId, handler, snapshot)) {
            onError(callback, "Dependency Installer Service not found");
            onError(callback, "Dependency Installer Service not found");
            return;
            return;
@@ -112,8 +118,8 @@ public class InstallDependencyHelper {
        IDependencyInstallerCallback serviceCallback =
        IDependencyInstallerCallback serviceCallback =
                new DependencyInstallerCallbackCallOnce(handler, callback, userId);
                new DependencyInstallerCallbackCallOnce(handler, callback, userId);
        boolean scheduleSuccess;
        boolean scheduleSuccess;
        synchronized (mRemoteServiceLock) {
        synchronized (mRemoteServices) {
            scheduleSuccess = mRemoteService.run(service -> {
            scheduleSuccess = mRemoteServices.get(userId).run(service -> {
                service.onDependenciesRequired(missing,
                service.onDependenciesRequired(missing,
                        new DependencyInstallerCallback(serviceCallback.asBinder()));
                        new DependencyInstallerCallback(serviceCallback.asBinder()));
            });
            });
@@ -149,15 +155,17 @@ public class InstallDependencyHelper {


    private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler,
    private boolean bindToDependencyInstallerIfNeeded(int userId, Handler handler,
            Computer snapshot) {
            Computer snapshot) {
        synchronized (mRemoteServiceLock) {
        synchronized (mRemoteServices) {
            if (mRemoteService != null) {
            if (mRemoteServices.containsKey(userId)) {
                if (DEBUG) {
                if (DEBUG) {
                    Slog.i(TAG, "DependencyInstallerService already bound");
                    Slog.i(TAG, "DependencyInstallerService for user " + userId + " already bound");
                }
                }
                return true;
                return true;
            }
            }
        }
        }


        Slog.i(TAG, "Attempting to bind to Dependency Installer Service for user " + userId);

        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
        if (roleManager == null) {
        if (roleManager == null) {
            Slog.w(TAG, "Cannot find RoleManager system service");
            Slog.w(TAG, "Cannot find RoleManager system service");
@@ -166,7 +174,7 @@ public class InstallDependencyHelper {
        List<String> holders = roleManager.getRoleHoldersAsUser(
        List<String> holders = roleManager.getRoleHoldersAsUser(
                ROLE_SYSTEM_DEPENDENCY_INSTALLER, UserHandle.of(userId));
                ROLE_SYSTEM_DEPENDENCY_INSTALLER, UserHandle.of(userId));
        if (holders.isEmpty()) {
        if (holders.isEmpty()) {
            Slog.w(TAG, "No holders of ROLE_SYSTEM_DEPENDENCY_INSTALLER found");
            Slog.w(TAG, "No holders of ROLE_SYSTEM_DEPENDENCY_INSTALLER found for user " + userId);
            return false;
            return false;
        }
        }


@@ -178,6 +186,8 @@ public class InstallDependencyHelper {
                /*includeInstantApps*/ false, /*resolveForStart*/ false);
                /*includeInstantApps*/ false, /*resolveForStart*/ false);


        if (resolvedIntents.isEmpty()) {
        if (resolvedIntents.isEmpty()) {
            Slog.w(TAG, "No package holding ROLE_SYSTEM_DEPENDENCY_INSTALLER found for user "
                    + userId);
            return false;
            return false;
        }
        }


@@ -206,13 +216,14 @@ public class InstallDependencyHelper {
                };
                };




        synchronized (mRemoteServiceLock) {
        synchronized (mRemoteServices) {
            // Some other thread managed to connect to the service first
            // Some other thread managed to connect to the service first
            if (mRemoteService != null) {
            if (mRemoteServices.containsKey(userId)) {
                return true;
                return true;
            }
            }
            mRemoteService = serviceConnector;
            mRemoteServices.put(userId, serviceConnector);
            mRemoteService.setServiceLifecycleCallbacks(
            // Block the lock until we connect to the service
            serviceConnector.setServiceLifecycleCallbacks(
                new ServiceConnector.ServiceLifecycleCallbacks<>() {
                new ServiceConnector.ServiceLifecycleCallbacks<>() {
                    @Override
                    @Override
                    public void onDisconnected(@NonNull IDependencyInstallerService service) {
                    public void onDisconnected(@NonNull IDependencyInstallerService service) {
@@ -228,17 +239,18 @@ public class InstallDependencyHelper {
                    }
                    }


                    private void destroy() {
                    private void destroy() {
                        synchronized (mRemoteServiceLock) {
                        synchronized (mRemoteServices) {
                            if (mRemoteService != null) {
                            if (mRemoteServices.containsKey(userId)) {
                                mRemoteService.unbind();
                                mRemoteServices.get(userId).unbind();
                                mRemoteService = null;
                                mRemoteServices.remove(userId);
                            }
                            }
                        }
                        }
                    }
                    }


                });
                });
            AndroidFuture<IDependencyInstallerService> unusedFuture = mRemoteService.connect();
            AndroidFuture<IDependencyInstallerService> unusedFuture = serviceConnector.connect();
        }
        }
        Slog.i(TAG, "Successfully bound to Dependency Installer Service for user " + userId);
        return true;
        return true;
    }
    }


@@ -318,6 +330,7 @@ public class InstallDependencyHelper {
                Slog.d(TAG, "onAllDependenciesResolved started");
                Slog.d(TAG, "onAllDependenciesResolved started");
            }
            }


            try {
                // Before creating any tracker, validate the arguments
                // Before creating any tracker, validate the arguments
                ArraySet<Integer> validSessionIds = validateSessionIds(sessionIds);
                ArraySet<Integer> validSessionIds = validateSessionIds(sessionIds);


@@ -340,9 +353,18 @@ public class InstallDependencyHelper {


                    // Don't wait for sessions that finished already
                    // Don't wait for sessions that finished already
                    if (sessionInfo == null) {
                    if (sessionInfo == null) {
                        Binder.withCleanCallingIdentity(() -> {
                            notifySessionComplete(sessionId, /*success=*/ true);
                            notifySessionComplete(sessionId, /*success=*/ true);
                        });
                    }
                    }
                }
                }
            } catch (Exception e) {
                // Allow calling the callback again
                synchronized (this) {
                    mDependencyInstallerCallbackInvoked = false;
                }
                throw e;
            }
        }
        }


        @Override
        @Override
@@ -354,7 +376,10 @@ public class InstallDependencyHelper {
                }
                }
                mDependencyInstallerCallbackInvoked = true;
                mDependencyInstallerCallbackInvoked = true;
            }
            }

            Binder.withCleanCallingIdentity(() -> {
                onError(mCallback, "Failed to resolve all dependencies automatically");
                onError(mCallback, "Failed to resolve all dependencies automatically");
            });
        }
        }


        private ArraySet<Integer> validateSessionIds(int[] sessionIds) {
        private ArraySet<Integer> validateSessionIds(int[] sessionIds) {
@@ -369,7 +394,8 @@ public class InstallDependencyHelper {
                // Continue waiting if session exists and hasn't passed or failed yet.
                // Continue waiting if session exists and hasn't passed or failed yet.
                if (sessionInfo != null) {
                if (sessionInfo != null) {
                    if (sessionInfo.isSessionFailed) {
                    if (sessionInfo.isSessionFailed) {
                        throwValidationError("Session already finished: " + sessionId);
                        throw new IllegalArgumentException("Session already finished: "
                                + sessionId);
                    }
                    }


                    // Wait for session to finish install if it's not already successful.
                    // Wait for session to finish install if it's not already successful.
@@ -398,25 +424,17 @@ public class InstallDependencyHelper {
                        s -> s.sessionId == sessionId).findFirst().orElse(null);
                        s -> s.sessionId == sessionId).findFirst().orElse(null);


                if (sessionInfo == null) {
                if (sessionInfo == null) {
                    throwValidationError("Failed to find session: " + sessionId);
                    throw new IllegalArgumentException("Failed to find session: " + sessionId);
                }
                }


                // Historical session must have been successful, otherwise throw IAE.
                // Historical session must have been successful, otherwise throw IAE.
                if (!sessionInfo.isSessionApplied) {
                if (!sessionInfo.isSessionApplied) {
                    throwValidationError("Session already finished: " + sessionId);
                    throw new IllegalArgumentException("Session already finished: " + sessionId);
                }
                }
            }
            }


            return validSessionIds;
            return validSessionIds;
        }
        }

        private void throwValidationError(String msg) {
            // Allow client to invoke callback again.
            synchronized (this) {
                mDependencyInstallerCallbackInvoked = false;
            }
            throw new IllegalArgumentException(msg);
        }
    }
    }


    /**
    /**