Loading core/java/android/os/ParcelFileDescriptor.java +3 −2 Original line number Diff line number Diff line Loading @@ -616,10 +616,11 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static File getFile(FileDescriptor fd) throws IOException { try { final String path = Os.readlink("/proc/self/fd/" + fd.getInt$()); if (OsConstants.S_ISREG(Os.stat(path).st_mode)) { if (OsConstants.S_ISREG(Os.stat(path).st_mode) || OsConstants.S_ISCHR(Os.stat(path).st_mode)) { return new File(path); } else { throw new IOException("Not a regular file: " + path); throw new IOException("Not a regular file or character device: " + path); } } catch (ErrnoException e) { throw e.rethrowAsIOException(); Loading services/core/java/com/android/server/StorageManagerService.java +33 −3 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.storage.AppFuseBridge; import com.android.server.storage.StorageSessionController; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver; Loading Loading @@ -498,6 +499,9 @@ class StorageManagerService extends IStorageManager.Stub private final StorageManagerInternalImpl mStorageManagerInternal = new StorageManagerInternalImpl(); // Not guarded by a lock. private final StorageSessionController mStorageSessionController; class ObbState implements IBinder.DeathRecipient { public ObbState(String rawPath, String canonicalPath, int callingUid, IObbActionListener token, int nonce, String volId) { Loading Loading @@ -647,12 +651,20 @@ class StorageManagerService extends IStorageManager.Stub Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy"); break; } mount(vol); // TODO(b/135341433): Remove paranoid logging when FUSE is stable Slog.i(TAG, "Mounting volume " + vol); // TODO(b/135341433): Update to use new vold API that gets or mounts fuse fd // Ensure that we can pass user of a volume to the new API mStorageSessionController.onVolumeMounted(mCurrentUserId, mount(vol), vol); Slog.i(TAG, "Mounted volume " + vol); break; } case H_VOLUME_UNMOUNT: { final VolumeInfo vol = (VolumeInfo) msg.obj; unmount(vol); mStorageSessionController.onVolumeUnmounted(mCurrentUserId, vol); break; } case H_VOLUME_BROADCAST: { Loading Loading @@ -733,6 +745,7 @@ class StorageManagerService extends IStorageManager.Stub } } mVold.onUserRemoved(userId); mStorageSessionController.onUserRemoved(userId); } } catch (Exception e) { Slog.wtf(TAG, e); Loading Loading @@ -951,7 +964,10 @@ class StorageManagerService extends IStorageManager.Stub } try { // TODO(b/135341433): Remove paranoid logging when FUSE is stable Slog.i(TAG, "Resetting vold"); mVold.reset(); Slog.i(TAG, "Reset vold"); // Tell vold about all existing and started users for (UserInfo user : users) { Loading @@ -976,6 +992,7 @@ class StorageManagerService extends IStorageManager.Stub // staging area is ready so it's ready for zygote-forked apps to // bind mount against. try { mStorageSessionController.onUserStarted(userId); mVold.onUserStarted(userId); mStoraged.onUserStarted(userId); } catch (Exception e) { Loading Loading @@ -1516,6 +1533,12 @@ class StorageManagerService extends IStorageManager.Stub // Add OBB Action Handler to StorageManagerService thread. mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); mStorageSessionController = new StorageSessionController(mContext, userId -> { Slog.i(TAG, "Storage session ended for user: " + userId + ". Resetting..."); mHandler.obtainMessage(H_RESET).sendToTarget(); }); // Initialize the last-fstrim tracking if necessary File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); Loading Loading @@ -1814,11 +1837,18 @@ class StorageManagerService extends IStorageManager.Stub mount(vol); } private void mount(VolumeInfo vol) { private FileDescriptor mount(VolumeInfo vol) { try { mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); // TODO(b/135341433): Now, emulated (and private?) volumes are shared across users // This means the mountUserId on such volumes is USER_NULL. This breaks fuse which // requires a valid user to mount a volume. Create individual volumes per user in vold // and remove this property check int userId = SystemProperties.getBoolean("persist.sys.fuse", false) ? mCurrentUserId : vol.mountUserId; return mVold.mount(vol.id, vol.mountFlags, userId); } catch (Exception e) { Slog.wtf(TAG, e); return null; } } Loading services/core/java/com/android/server/storage/StorageSessionController.java 0 → 100644 +217 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.storage; import android.Manifest; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.ParcelFileDescriptor; import android.os.storage.VolumeInfo; import android.provider.MediaStore; import android.service.storage.ExternalStorageService; 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.io.IOException; /** * Controls storage sessions for users initiated by the {@link StorageManagerService}. * Each user on the device will be represented by a {@link StorageUserConnection}. */ public final class StorageSessionController { private static final String TAG = "StorageSessionController"; private final Object mLock = new Object(); private final Context mContext; private final Callback mCallback; @GuardedBy("mLock") private ComponentName mExternalStorageServiceComponent; @GuardedBy("mLock") private final SparseArray<StorageUserConnection> mConnections = new SparseArray<>(); public StorageSessionController(Context context, Callback callback) { mContext = Preconditions.checkNotNull(context); mCallback = Preconditions.checkNotNull(callback); } /** * Starts a storage session associated with {@code deviceFd} for {@code vol}. * Does nothing if a session is already started or starting. If the user associated with * {@code vol} is not yet ready, the session will be retried {@link #onUserStarted}. * * A session must be ended with {@link #endSession} when no longer required. */ public void onVolumeMounted(int userId, FileDescriptor deviceFd, VolumeInfo vol) { if (deviceFd == null) { Slog.w(TAG, "Null device fd. Session not started for " + vol); return; } // Get realpath for the fd, paths that are not /dev/null need additional // setup by the ExternalStorageService before they can be ready String realPath; try { realPath = ParcelFileDescriptor.getFile(deviceFd).getPath(); } catch (IOException e) { Slog.wtf(TAG, "Could not get real path from fd: " + deviceFd, e); return; } if ("/dev/null".equals(realPath)) { Slog.i(TAG, "Volume ready for use: " + vol); return; } synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection == null) { Slog.i(TAG, "Creating new session for vol: " + vol); connection = new StorageUserConnection(mContext, userId, this); mConnections.put(userId, connection); } try { Slog.i(TAG, "Starting session for vol: " + vol); connection.startSession(deviceFd, vol); } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to start session for vol: " + vol, e); } } } /** * Ends a storage session for {@code vol}. Does nothing if the session is already * ended or ending. Ending a session discards all resources associated with that session. */ public void onVolumeUnmounted(int userId, VolumeInfo vol) { synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection != null) { Slog.i(TAG, "Ending session for vol: " + vol); try { if (connection.endSession(vol)) { mConnections.remove(userId); } } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to end session for vol: " + vol, e); } } else { Slog.w(TAG, "Session already ended for vol: " + vol); } } } /** Restarts all sessions for {@code userId}. */ public void onUserStarted(int userId) { synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection != null) { try { Slog.i(TAG, "Restarting all sessions for user: " + userId); connection.startAllSessions(); } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to start all sessions", e); } } else { // TODO(b/135341433): What does this mean in multi-user } } } /** Ends all sessions for {@code userId}. */ public void onUserRemoved(int userId) { synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection != null) { try { Slog.i(TAG, "Ending all sessions for user: " + userId); connection.endAllSessions(); mConnections.remove(userId); } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to end all sessions", e); } } else { // TODO(b/135341433): What does this mean in multi-user } } } /** Returns the {@link ExternalStorageService} component name. */ @Nullable public ComponentName getExternalStorageServiceComponentName() { synchronized (mLock) { if (mExternalStorageServiceComponent == null) { ProviderInfo provider = mContext.getPackageManager().resolveContentProvider( MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_SYSTEM_ONLY); if (provider == null) { Slog.e(TAG, "No valid MediaStore provider found."); } String packageName = provider.applicationInfo.packageName; Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE); intent.setPackage(packageName); ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); if (resolveInfo == null || resolveInfo.serviceInfo == null) { Slog.e(TAG, "No valid ExternalStorageService component found."); return null; } ServiceInfo serviceInfo = resolveInfo.serviceInfo; ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); if (!Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE .equals(serviceInfo.permission)) { Slog.e(TAG, name.flattenToShortString() + " does not require permission " + Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE); return null; } mExternalStorageServiceComponent = name; } return mExternalStorageServiceComponent; } } /** Returns the {@link StorageManagerService} callback. */ public Callback getCallback() { return mCallback; } /** Callback to listen to session events from the {@link StorageSessionController}. */ public interface Callback { /** Called when a {@link StorageUserConnection} is disconnected. */ void onUserDisconnected(int userId); } /** Exception thrown when communication with the {@link ExternalStorageService}. */ public static class ExternalStorageServiceException extends Exception { public ExternalStorageServiceException(Throwable cause) { super(cause); } } } services/core/java/com/android/server/storage/StorageUserConnection.java 0 → 100644 +319 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/os/ParcelFileDescriptor.java +3 −2 Original line number Diff line number Diff line Loading @@ -616,10 +616,11 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static File getFile(FileDescriptor fd) throws IOException { try { final String path = Os.readlink("/proc/self/fd/" + fd.getInt$()); if (OsConstants.S_ISREG(Os.stat(path).st_mode)) { if (OsConstants.S_ISREG(Os.stat(path).st_mode) || OsConstants.S_ISCHR(Os.stat(path).st_mode)) { return new File(path); } else { throw new IOException("Not a regular file: " + path); throw new IOException("Not a regular file or character device: " + path); } } catch (ErrnoException e) { throw e.rethrowAsIOException(); Loading
services/core/java/com/android/server/StorageManagerService.java +33 −3 Original line number Diff line number Diff line Loading @@ -140,6 +140,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.storage.AppFuseBridge; import com.android.server.storage.StorageSessionController; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver; Loading Loading @@ -498,6 +499,9 @@ class StorageManagerService extends IStorageManager.Stub private final StorageManagerInternalImpl mStorageManagerInternal = new StorageManagerInternalImpl(); // Not guarded by a lock. private final StorageSessionController mStorageSessionController; class ObbState implements IBinder.DeathRecipient { public ObbState(String rawPath, String canonicalPath, int callingUid, IObbActionListener token, int nonce, String volId) { Loading Loading @@ -647,12 +651,20 @@ class StorageManagerService extends IStorageManager.Stub Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy"); break; } mount(vol); // TODO(b/135341433): Remove paranoid logging when FUSE is stable Slog.i(TAG, "Mounting volume " + vol); // TODO(b/135341433): Update to use new vold API that gets or mounts fuse fd // Ensure that we can pass user of a volume to the new API mStorageSessionController.onVolumeMounted(mCurrentUserId, mount(vol), vol); Slog.i(TAG, "Mounted volume " + vol); break; } case H_VOLUME_UNMOUNT: { final VolumeInfo vol = (VolumeInfo) msg.obj; unmount(vol); mStorageSessionController.onVolumeUnmounted(mCurrentUserId, vol); break; } case H_VOLUME_BROADCAST: { Loading Loading @@ -733,6 +745,7 @@ class StorageManagerService extends IStorageManager.Stub } } mVold.onUserRemoved(userId); mStorageSessionController.onUserRemoved(userId); } } catch (Exception e) { Slog.wtf(TAG, e); Loading Loading @@ -951,7 +964,10 @@ class StorageManagerService extends IStorageManager.Stub } try { // TODO(b/135341433): Remove paranoid logging when FUSE is stable Slog.i(TAG, "Resetting vold"); mVold.reset(); Slog.i(TAG, "Reset vold"); // Tell vold about all existing and started users for (UserInfo user : users) { Loading @@ -976,6 +992,7 @@ class StorageManagerService extends IStorageManager.Stub // staging area is ready so it's ready for zygote-forked apps to // bind mount against. try { mStorageSessionController.onUserStarted(userId); mVold.onUserStarted(userId); mStoraged.onUserStarted(userId); } catch (Exception e) { Loading Loading @@ -1516,6 +1533,12 @@ class StorageManagerService extends IStorageManager.Stub // Add OBB Action Handler to StorageManagerService thread. mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); mStorageSessionController = new StorageSessionController(mContext, userId -> { Slog.i(TAG, "Storage session ended for user: " + userId + ". Resetting..."); mHandler.obtainMessage(H_RESET).sendToTarget(); }); // Initialize the last-fstrim tracking if necessary File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); Loading Loading @@ -1814,11 +1837,18 @@ class StorageManagerService extends IStorageManager.Stub mount(vol); } private void mount(VolumeInfo vol) { private FileDescriptor mount(VolumeInfo vol) { try { mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); // TODO(b/135341433): Now, emulated (and private?) volumes are shared across users // This means the mountUserId on such volumes is USER_NULL. This breaks fuse which // requires a valid user to mount a volume. Create individual volumes per user in vold // and remove this property check int userId = SystemProperties.getBoolean("persist.sys.fuse", false) ? mCurrentUserId : vol.mountUserId; return mVold.mount(vol.id, vol.mountFlags, userId); } catch (Exception e) { Slog.wtf(TAG, e); return null; } } Loading
services/core/java/com/android/server/storage/StorageSessionController.java 0 → 100644 +217 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.storage; import android.Manifest; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.ParcelFileDescriptor; import android.os.storage.VolumeInfo; import android.provider.MediaStore; import android.service.storage.ExternalStorageService; 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.io.IOException; /** * Controls storage sessions for users initiated by the {@link StorageManagerService}. * Each user on the device will be represented by a {@link StorageUserConnection}. */ public final class StorageSessionController { private static final String TAG = "StorageSessionController"; private final Object mLock = new Object(); private final Context mContext; private final Callback mCallback; @GuardedBy("mLock") private ComponentName mExternalStorageServiceComponent; @GuardedBy("mLock") private final SparseArray<StorageUserConnection> mConnections = new SparseArray<>(); public StorageSessionController(Context context, Callback callback) { mContext = Preconditions.checkNotNull(context); mCallback = Preconditions.checkNotNull(callback); } /** * Starts a storage session associated with {@code deviceFd} for {@code vol}. * Does nothing if a session is already started or starting. If the user associated with * {@code vol} is not yet ready, the session will be retried {@link #onUserStarted}. * * A session must be ended with {@link #endSession} when no longer required. */ public void onVolumeMounted(int userId, FileDescriptor deviceFd, VolumeInfo vol) { if (deviceFd == null) { Slog.w(TAG, "Null device fd. Session not started for " + vol); return; } // Get realpath for the fd, paths that are not /dev/null need additional // setup by the ExternalStorageService before they can be ready String realPath; try { realPath = ParcelFileDescriptor.getFile(deviceFd).getPath(); } catch (IOException e) { Slog.wtf(TAG, "Could not get real path from fd: " + deviceFd, e); return; } if ("/dev/null".equals(realPath)) { Slog.i(TAG, "Volume ready for use: " + vol); return; } synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection == null) { Slog.i(TAG, "Creating new session for vol: " + vol); connection = new StorageUserConnection(mContext, userId, this); mConnections.put(userId, connection); } try { Slog.i(TAG, "Starting session for vol: " + vol); connection.startSession(deviceFd, vol); } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to start session for vol: " + vol, e); } } } /** * Ends a storage session for {@code vol}. Does nothing if the session is already * ended or ending. Ending a session discards all resources associated with that session. */ public void onVolumeUnmounted(int userId, VolumeInfo vol) { synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection != null) { Slog.i(TAG, "Ending session for vol: " + vol); try { if (connection.endSession(vol)) { mConnections.remove(userId); } } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to end session for vol: " + vol, e); } } else { Slog.w(TAG, "Session already ended for vol: " + vol); } } } /** Restarts all sessions for {@code userId}. */ public void onUserStarted(int userId) { synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection != null) { try { Slog.i(TAG, "Restarting all sessions for user: " + userId); connection.startAllSessions(); } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to start all sessions", e); } } else { // TODO(b/135341433): What does this mean in multi-user } } } /** Ends all sessions for {@code userId}. */ public void onUserRemoved(int userId) { synchronized (mLock) { StorageUserConnection connection = mConnections.get(userId); if (connection != null) { try { Slog.i(TAG, "Ending all sessions for user: " + userId); connection.endAllSessions(); mConnections.remove(userId); } catch (ExternalStorageServiceException e) { Slog.e(TAG, "Failed to end all sessions", e); } } else { // TODO(b/135341433): What does this mean in multi-user } } } /** Returns the {@link ExternalStorageService} component name. */ @Nullable public ComponentName getExternalStorageServiceComponentName() { synchronized (mLock) { if (mExternalStorageServiceComponent == null) { ProviderInfo provider = mContext.getPackageManager().resolveContentProvider( MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_SYSTEM_ONLY); if (provider == null) { Slog.e(TAG, "No valid MediaStore provider found."); } String packageName = provider.applicationInfo.packageName; Intent intent = new Intent(ExternalStorageService.SERVICE_INTERFACE); intent.setPackage(packageName); ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); if (resolveInfo == null || resolveInfo.serviceInfo == null) { Slog.e(TAG, "No valid ExternalStorageService component found."); return null; } ServiceInfo serviceInfo = resolveInfo.serviceInfo; ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); if (!Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE .equals(serviceInfo.permission)) { Slog.e(TAG, name.flattenToShortString() + " does not require permission " + Manifest.permission.BIND_EXTERNAL_STORAGE_SERVICE); return null; } mExternalStorageServiceComponent = name; } return mExternalStorageServiceComponent; } } /** Returns the {@link StorageManagerService} callback. */ public Callback getCallback() { return mCallback; } /** Callback to listen to session events from the {@link StorageSessionController}. */ public interface Callback { /** Called when a {@link StorageUserConnection} is disconnected. */ void onUserDisconnected(int userId); } /** Exception thrown when communication with the {@link ExternalStorageService}. */ public static class ExternalStorageServiceException extends Exception { public ExternalStorageServiceException(Throwable cause) { super(cause); } } }
services/core/java/com/android/server/storage/StorageUserConnection.java 0 → 100644 +319 −0 File added.Preview size limit exceeded, changes collapsed. Show changes