From 7924a5169016a039f0b977003a74ffd9b20833cc Mon Sep 17 00:00:00 2001 From: Zim Date: Wed, 15 Jan 2020 15:23:54 +0000 Subject: [PATCH] Recover from FUSE session starting failures Vold calls into StorageManagerService which in turn calls into the FUSE daemon to start the session. If the system_server is very busy e.g right after an OTA, the session may not start quickly enough and we will timeout, causing vold to fail the mount. Previously, the only solution would be for the user to reboot. Now, we naively retry every 10s. Bug: 147610762 Test: Causing an artificial timeout when starting a session for the firs time, verified we recover after a minute Change-Id: Ifdb2384696e85b6774a12394798fca8eb75a584e --- .../android/server/StorageManagerService.java | 19 ++++++++++++++++--- .../storage/StorageSessionController.java | 10 ++++------ .../server/storage/StorageUserConnection.java | 3 ++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index db542145a750d..a0606dd639c54 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -44,6 +44,7 @@ import static com.android.internal.util.XmlUtils.readStringAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; +import static com.android.server.storage.StorageUserConnection.REMOTE_TIMEOUT_SECONDS; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -1985,16 +1986,28 @@ class StorageManagerService extends IStorageManager.Stub Slog.i(TAG, "Mounting volume " + vol); mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() { @Override - public boolean onVolumeChecking(FileDescriptor deviceFd, String path, + public boolean onVolumeChecking(FileDescriptor fd, String path, String internalPath) { vol.path = path; vol.internalPath = internalPath; + ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd); try { - mStorageSessionController.onVolumeMount(deviceFd, vol); + mStorageSessionController.onVolumeMount(pfd, vol); return true; } catch (ExternalStorageServiceException e) { - Slog.i(TAG, "Failed to mount volume " + vol, e); + Slog.e(TAG, "Failed to mount volume " + vol, e); + + Slog.i(TAG, "Scheduling reset in one minute"); + mHandler.removeMessages(H_RESET); + mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET), + TimeUnit.SECONDS.toMillis(REMOTE_TIMEOUT_SECONDS * 2)); return false; + } finally { + try { + pfd.close(); + } catch (Exception e) { + Slog.e(TAG, "Failed to close FUSE device fd", e); + } } } }); diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index f4fb93a0ce23c..baef5d6ef2a0f 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -40,9 +40,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; -import java.io.FileDescriptor; import java.util.Objects; /** @@ -82,7 +80,7 @@ public final class StorageSessionController { * @throws ExternalStorageServiceException if the session fails to start * @throws IllegalStateException if a session has already been created for {@code vol} */ - public void onVolumeMount(FileDescriptor deviceFd, VolumeInfo vol) + public void onVolumeMount(ParcelFileDescriptor deviceFd, VolumeInfo vol) throws ExternalStorageServiceException { if (!shouldHandle(vol)) { return; @@ -102,8 +100,8 @@ public final class StorageSessionController { mConnections.put(userId, connection); } Slog.i(TAG, "Creating and starting session with id: " + sessionId); - connection.startSession(sessionId, new ParcelFileDescriptor(deviceFd), - vol.getPath().getPath(), vol.getInternalPath().getPath()); + connection.startSession(sessionId, deviceFd, vol.getPath().getPath(), + vol.getInternalPath().getPath()); } } @@ -185,7 +183,7 @@ public final class StorageSessionController { * 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 { + public void onUserStopping(int userId) { if (!shouldHandle(null)) { return; } diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java index c02ded83586a9..dd18f4e5ab174 100644 --- a/services/core/java/com/android/server/storage/StorageUserConnection.java +++ b/services/core/java/com/android/server/storage/StorageUserConnection.java @@ -60,7 +60,8 @@ import java.util.concurrent.TimeoutException; */ public final class StorageUserConnection { private static final String TAG = "StorageUserConnection"; - private static final int REMOTE_TIMEOUT_SECONDS = 15; + + public static final int REMOTE_TIMEOUT_SECONDS = 5; private final Object mLock = new Object(); private final Context mContext;