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

Commit 7084c6a9 authored by Martijn Coenen's avatar Martijn Coenen
Browse files

Tell StorageSessionController when a user is stopping.

When a user is stopping, all processes running under that user will be
killed, including the MediaProvider process that hosts the FUSE
filesystem. This means that the binding to the MediaProvider will fail;
to prevent us from trying to rebind needlessly, let the
StorageSessionController know that a user is about to be stopped.

Bug: 137890172
Test: test started and stopping a user; no attempts to rebind.
Change-Id: Ie2810464f20f13a9700ead506c82731f603ab374
parent 5356d348
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
@@ -253,6 +254,11 @@ class StorageManagerService extends IStorageManager.Stub
        public void onCleanupUser(int userHandle) {
            mStorageManagerService.onCleanupUser(userHandle);
        }

        @Override
        public void onStopUser(int userHandle) {
            mStorageManagerService.onStopUser(userHandle);
        }
    }

    private static final boolean DEBUG_EVENTS = false;
@@ -1075,6 +1081,15 @@ class StorageManagerService extends IStorageManager.Stub
        }
    }

    private void onStopUser(int userId) {
        Slog.i(TAG, "onStopUser " + userId);
        try {
            mStorageSessionController.onUserStopping(userId);
        } catch (Exception e) {
            Slog.wtf(TAG, e);
        }
    }

    private boolean supportsBlockCheckpoint() throws RemoteException {
        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
        return mVold.supportsBlockCheckpoint();
@@ -1309,6 +1324,15 @@ class StorageManagerService extends IStorageManager.Stub
            Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
            return;
        }
        final ActivityManagerInternal amInternal =
                LocalServices.getService(ActivityManagerInternal.class);

        if (mIsFuseEnabled && vol.mountUserId >= 0
                && !amInternal.isUserRunning(vol.mountUserId, 0)) {
            Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
                    + Integer.toString(vol.mountUserId) + " is no longer running.");
            return;
        }

        if (vol.type == VolumeInfo.TYPE_EMULATED) {
            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+25 −1
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;

import java.io.FileDescriptor;
import java.io.IOException;

/**
 * Controls storage sessions for users initiated by the {@link StorageManagerService}.
@@ -201,6 +200,31 @@ public final class StorageSessionController {
        }
    }

    /**
     * Called when a user is in the process is being stopped.
     *
     * Does nothing if {@link #shouldHandle} is {@code false}
     *
     * This call removes all sessions for the user that is being stopped;
     * this will make sure that we don't rebind to the service needlessly.
     */
    public void onUserStopping(int userId) throws ExternalStorageServiceException {
        if (!shouldHandle(null)) {
            return;
        }
        StorageUserConnection connection = null;
        synchronized (mLock) {
            connection = mConnections.get(userId);
        }

        if (connection != null) {
            Slog.i(TAG, "Closing all sessions for user: " + userId);
            connection.removeAllSessions();
        } else {
            Slog.w(TAG, "No connection found for user: " + userId);
        }
    }

    /**
     * Resets all sessions for all users and waits for exit. This may kill the
     * {@link ExternalStorageservice} for a user if necessary to ensure all state has been reset.
+30 −0
Original line number Diff line number Diff line
@@ -152,6 +152,13 @@ public final class StorageUserConnection {

    /** Starts all available sessions for a user without blocking. Any failures will be ignored. */
    public void startAllSessions() {
        synchronized (mLock) {
            if (mSessions.isEmpty()) {
                // No point bringing up the remote if we don't have any sessions to start
                return;
            }
        }

        try {
            prepareRemote();
        } catch (ExternalStorageServiceException e) {
@@ -172,6 +179,29 @@ public final class StorageUserConnection {
        }
    }

    /**
     * Removes all sessions, without waiting.
     */
    public void removeAllSessions() {
        synchronized (mLock) {
            closeAllSessions();
            Slog.i(TAG, "Removing  " + mSessions.size() + " sessions for user: " + mUserId + "...");
            mSessions.clear();
        }
    }

    /**
     * Closes all sessions, but doesn't remove them or unbind the service.
     */
    public void closeAllSessions() {
        synchronized (mLock) {
            Slog.i(TAG, "Closing " + mSessions.size() + " sessions for user: " + mUserId + "...");
            for (Session session : mSessions.values()) {
                session.close();
            }
        }
    }

    /**
     * Closes the connection to the {@link ExternalStorageService}. The connection will typically
     * be restarted after close.