Merge "Use futures for binding and talking to the ExternalStorageService." into rvc-qpr-dev am: 61e7956648
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13713298 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Ia4483ed25dddde3dba6f9d64c4dc40a6cdd67c55
This commit is contained in:
@@ -23,6 +23,7 @@ import static android.service.storage.ExternalStorageService.FLAG_SESSION_TYPE_F
|
|||||||
import static com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
|
import static com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
|
||||||
|
|
||||||
import android.annotation.MainThread;
|
import android.annotation.MainThread;
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -34,13 +35,12 @@ import android.os.IBinder;
|
|||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.ParcelableException;
|
import android.os.ParcelableException;
|
||||||
import android.os.RemoteCallback;
|
import android.os.RemoteCallback;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManagerInternal;
|
|
||||||
import android.os.storage.StorageManagerInternal;
|
import android.os.storage.StorageManagerInternal;
|
||||||
import android.os.storage.StorageVolume;
|
import android.os.storage.StorageVolume;
|
||||||
import android.service.storage.ExternalStorageService;
|
import android.service.storage.ExternalStorageService;
|
||||||
import android.service.storage.IExternalStorageService;
|
import android.service.storage.IExternalStorageService;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
@@ -48,14 +48,14 @@ import com.android.internal.util.Preconditions;
|
|||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controls the lifecycle of the {@link ActiveConnection} to an {@link ExternalStorageService}
|
* Controls the lifecycle of the {@link ActiveConnection} to an {@link ExternalStorageService}
|
||||||
@@ -66,25 +66,20 @@ public final class StorageUserConnection {
|
|||||||
|
|
||||||
private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
|
private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
|
||||||
|
|
||||||
private final Object mLock = new Object();
|
private final Object mSessionsLock = new Object();
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final int mUserId;
|
private final int mUserId;
|
||||||
private final StorageSessionController mSessionController;
|
private final StorageSessionController mSessionController;
|
||||||
private final ActiveConnection mActiveConnection = new ActiveConnection();
|
private final ActiveConnection mActiveConnection = new ActiveConnection();
|
||||||
private final boolean mIsDemoUser;
|
|
||||||
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
|
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
|
||||||
@GuardedBy("mLock") @Nullable private HandlerThread mHandlerThread;
|
private final HandlerThread mHandlerThread;
|
||||||
|
|
||||||
public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
|
public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
|
||||||
mContext = Objects.requireNonNull(context);
|
mContext = Objects.requireNonNull(context);
|
||||||
mUserId = Preconditions.checkArgumentNonnegative(userId);
|
mUserId = Preconditions.checkArgumentNonnegative(userId);
|
||||||
mSessionController = controller;
|
mSessionController = controller;
|
||||||
mIsDemoUser = LocalServices.getService(UserManagerInternal.class)
|
mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
|
||||||
.getUserInfo(userId).isDemo();
|
mHandlerThread.start();
|
||||||
if (mIsDemoUser) {
|
|
||||||
mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
|
|
||||||
mHandlerThread.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,13 +96,12 @@ public final class StorageUserConnection {
|
|||||||
Objects.requireNonNull(upperPath);
|
Objects.requireNonNull(upperPath);
|
||||||
Objects.requireNonNull(lowerPath);
|
Objects.requireNonNull(lowerPath);
|
||||||
|
|
||||||
prepareRemote();
|
Session session = new Session(sessionId, upperPath, lowerPath);
|
||||||
synchronized (mLock) {
|
synchronized (mSessionsLock) {
|
||||||
Preconditions.checkArgument(!mSessions.containsKey(sessionId));
|
Preconditions.checkArgument(!mSessions.containsKey(sessionId));
|
||||||
Session session = new Session(sessionId, upperPath, lowerPath);
|
|
||||||
mSessions.put(sessionId, session);
|
mSessions.put(sessionId, session);
|
||||||
mActiveConnection.startSessionLocked(session, pfd);
|
|
||||||
}
|
}
|
||||||
|
mActiveConnection.startSession(session, pfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,10 +115,13 @@ public final class StorageUserConnection {
|
|||||||
Objects.requireNonNull(sessionId);
|
Objects.requireNonNull(sessionId);
|
||||||
Objects.requireNonNull(vol);
|
Objects.requireNonNull(vol);
|
||||||
|
|
||||||
prepareRemote();
|
synchronized (mSessionsLock) {
|
||||||
synchronized (mLock) {
|
if (!mSessions.containsKey(sessionId)) {
|
||||||
mActiveConnection.notifyVolumeStateChangedLocked(sessionId, vol);
|
Slog.i(TAG, "No session found for sessionId: " + sessionId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
mActiveConnection.notifyVolumeStateChanged(sessionId, vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,7 +132,7 @@ public final class StorageUserConnection {
|
|||||||
* with {@link #waitForExit}.
|
* with {@link #waitForExit}.
|
||||||
**/
|
**/
|
||||||
public Session removeSession(String sessionId) {
|
public Session removeSession(String sessionId) {
|
||||||
synchronized (mLock) {
|
synchronized (mSessionsLock) {
|
||||||
return mSessions.remove(sessionId);
|
return mSessions.remove(sessionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,10 +150,7 @@ public final class StorageUserConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Slog.i(TAG, "Waiting for session end " + session + " ...");
|
Slog.i(TAG, "Waiting for session end " + session + " ...");
|
||||||
prepareRemote();
|
mActiveConnection.endSession(session);
|
||||||
synchronized (mLock) {
|
|
||||||
mActiveConnection.endSessionLocked(session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Restarts all available sessions for a user without blocking.
|
/** Restarts all available sessions for a user without blocking.
|
||||||
@@ -164,7 +158,7 @@ public final class StorageUserConnection {
|
|||||||
* Any failures will be ignored.
|
* Any failures will be ignored.
|
||||||
**/
|
**/
|
||||||
public void resetUserSessions() {
|
public void resetUserSessions() {
|
||||||
synchronized (mLock) {
|
synchronized (mSessionsLock) {
|
||||||
if (mSessions.isEmpty()) {
|
if (mSessions.isEmpty()) {
|
||||||
// Nothing to reset if we have no sessions to restart; we typically
|
// Nothing to reset if we have no sessions to restart; we typically
|
||||||
// hit this path if the user was consciously shut down.
|
// hit this path if the user was consciously shut down.
|
||||||
@@ -179,7 +173,7 @@ public final class StorageUserConnection {
|
|||||||
* Removes all sessions, without waiting.
|
* Removes all sessions, without waiting.
|
||||||
*/
|
*/
|
||||||
public void removeAllSessions() {
|
public void removeAllSessions() {
|
||||||
synchronized (mLock) {
|
synchronized (mSessionsLock) {
|
||||||
Slog.i(TAG, "Removing " + mSessions.size() + " sessions for user: " + mUserId + "...");
|
Slog.i(TAG, "Removing " + mSessions.size() + " sessions for user: " + mUserId + "...");
|
||||||
mSessions.clear();
|
mSessions.clear();
|
||||||
}
|
}
|
||||||
@@ -191,68 +185,54 @@ public final class StorageUserConnection {
|
|||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
mActiveConnection.close();
|
mActiveConnection.close();
|
||||||
if (mIsDemoUser) {
|
mHandlerThread.quit();
|
||||||
mHandlerThread.quit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns all created sessions. */
|
/** Returns all created sessions. */
|
||||||
public Set<String> getAllSessionIds() {
|
public Set<String> getAllSessionIds() {
|
||||||
synchronized (mLock) {
|
synchronized (mSessionsLock) {
|
||||||
return new HashSet<>(mSessions.keySet());
|
return new HashSet<>(mSessions.keySet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareRemote() throws ExternalStorageServiceException {
|
@FunctionalInterface
|
||||||
try {
|
interface AsyncStorageServiceCall {
|
||||||
waitForLatch(mActiveConnection.bind(), "remote_prepare_user " + mUserId);
|
void run(@NonNull IExternalStorageService service, RemoteCallback callback) throws
|
||||||
} catch (IllegalStateException | TimeoutException e) {
|
RemoteException;
|
||||||
throw new ExternalStorageServiceException("Failed to prepare remote", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException {
|
|
||||||
try {
|
|
||||||
if (!latch.await(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
|
|
||||||
// TODO(b/140025078): Call ActivityManager ANR API?
|
|
||||||
Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
|
|
||||||
throw new TimeoutException("Latch wait for " + reason + " elapsed");
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new IllegalStateException("Latch wait for " + reason + " interrupted");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ActiveConnection implements AutoCloseable {
|
private final class ActiveConnection implements AutoCloseable {
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
// Lifecycle connection to the external storage service, needed to unbind.
|
// Lifecycle connection to the external storage service, needed to unbind.
|
||||||
@GuardedBy("mLock") @Nullable private ServiceConnection mServiceConnection;
|
@GuardedBy("mLock") @Nullable private ServiceConnection mServiceConnection;
|
||||||
// True if we are connecting, either bound or binding
|
|
||||||
// False && mRemote != null means we are connected
|
// A future that holds the remote interface
|
||||||
// False && mRemote == null means we are neither connecting nor connected
|
@GuardedBy("mLock")
|
||||||
@GuardedBy("mLock") @Nullable private boolean mIsConnecting;
|
@Nullable private CompletableFuture<IExternalStorageService> mRemoteFuture;
|
||||||
// Binder object representing the external storage service.
|
|
||||||
// Non-null indicates we are connected
|
// A list of outstanding futures for async calls, for which we are still waiting
|
||||||
@GuardedBy("mLock") @Nullable private IExternalStorageService mRemote;
|
// for a callback. Used to unblock waiters if the service dies.
|
||||||
// Exception, if any, thrown from #startSessionLocked or #endSessionLocked
|
@GuardedBy("mLock")
|
||||||
// Local variables cannot be referenced from a lambda expression :( so we
|
private ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>();
|
||||||
// save the exception received in the callback here. Since we guard access
|
|
||||||
// (and clear the exception state) with the same lock which we hold during
|
|
||||||
// the entire transaction, there is no risk of race.
|
|
||||||
@GuardedBy("mLock") @Nullable private ParcelableException mLastException;
|
|
||||||
// Not guarded by any lock intentionally and non final because we cannot
|
|
||||||
// reset latches so need to create a new one after one use
|
|
||||||
private CountDownLatch mLatch;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
ServiceConnection oldConnection = null;
|
ServiceConnection oldConnection = null;
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
Slog.i(TAG, "Closing connection for user " + mUserId);
|
Slog.i(TAG, "Closing connection for user " + mUserId);
|
||||||
mIsConnecting = false;
|
|
||||||
oldConnection = mServiceConnection;
|
oldConnection = mServiceConnection;
|
||||||
mServiceConnection = null;
|
mServiceConnection = null;
|
||||||
mRemote = null;
|
if (mRemoteFuture != null) {
|
||||||
|
// Let folks who are waiting for the connection know it ain't gonna happen
|
||||||
|
mRemoteFuture.cancel(true);
|
||||||
|
mRemoteFuture = null;
|
||||||
|
}
|
||||||
|
// Let folks waiting for callbacks from the remote know it ain't gonna happen
|
||||||
|
for (CompletableFuture<Void> op : mOutstandingOps) {
|
||||||
|
op.cancel(true);
|
||||||
|
}
|
||||||
|
mOutstandingOps.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldConnection != null) {
|
if (oldConnection != null) {
|
||||||
@@ -266,37 +246,37 @@ public final class StorageUserConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActiveLocked(Session session) {
|
private void waitForAsync(AsyncStorageServiceCall asyncCall) throws Exception {
|
||||||
if (!session.isInitialisedLocked()) {
|
CompletableFuture<IExternalStorageService> serviceFuture = connectIfNeeded();
|
||||||
Slog.i(TAG, "Session not initialised " + session);
|
CompletableFuture<Void> opFuture = new CompletableFuture<>();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mRemote == null) {
|
try {
|
||||||
throw new IllegalStateException("Valid session with inactive connection");
|
synchronized (mLock) {
|
||||||
|
mOutstandingOps.add(opFuture);
|
||||||
|
}
|
||||||
|
serviceFuture.thenCompose(service -> {
|
||||||
|
try {
|
||||||
|
asyncCall.run(service,
|
||||||
|
new RemoteCallback(result -> setResult(result, opFuture)));
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
opFuture.completeExceptionally(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return opFuture;
|
||||||
|
}).get(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
|
} finally {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mOutstandingOps.remove(opFuture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startSessionLocked(Session session, ParcelFileDescriptor fd)
|
public void startSession(Session session, ParcelFileDescriptor fd)
|
||||||
throws ExternalStorageServiceException {
|
throws ExternalStorageServiceException {
|
||||||
if (!isActiveLocked(session)) {
|
|
||||||
try {
|
|
||||||
fd.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
try {
|
try {
|
||||||
mRemote.startSession(session.sessionId,
|
waitForAsync((service, callback) -> service.startSession(session.sessionId,
|
||||||
FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
|
FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
|
||||||
fd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
|
fd, session.upperPath, session.lowerPath, callback));
|
||||||
setResultLocked(latch, result)));
|
|
||||||
waitForLatch(latch, "start_session " + session);
|
|
||||||
maybeThrowExceptionLocked();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ExternalStorageServiceException("Failed to start session: " + session, e);
|
throw new ExternalStorageServiceException("Failed to start session: " + session, e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -308,73 +288,49 @@ public final class StorageUserConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endSessionLocked(Session session) throws ExternalStorageServiceException {
|
public void endSession(Session session) throws ExternalStorageServiceException {
|
||||||
if (!isActiveLocked(session)) {
|
|
||||||
// Nothing to end, not started yet
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
try {
|
try {
|
||||||
mRemote.endSession(session.sessionId, new RemoteCallback(result ->
|
waitForAsync((service, callback) ->
|
||||||
setResultLocked(latch, result)));
|
service.endSession(session.sessionId, callback));
|
||||||
waitForLatch(latch, "end_session " + session);
|
|
||||||
maybeThrowExceptionLocked();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ExternalStorageServiceException("Failed to end session: " + session, e);
|
throw new ExternalStorageServiceException("Failed to end session: " + session, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyVolumeStateChangedLocked(String sessionId, StorageVolume vol) throws
|
|
||||||
|
public void notifyVolumeStateChanged(String sessionId, StorageVolume vol) throws
|
||||||
ExternalStorageServiceException {
|
ExternalStorageServiceException {
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
try {
|
try {
|
||||||
mRemote.notifyVolumeStateChanged(sessionId, vol, new RemoteCallback(
|
waitForAsync((service, callback) ->
|
||||||
result -> setResultLocked(latch, result)));
|
service.notifyVolumeStateChanged(sessionId, vol, callback));
|
||||||
waitForLatch(latch, "notify_volume_state_changed " + vol);
|
|
||||||
maybeThrowExceptionLocked();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ExternalStorageServiceException("Failed to notify volume state changed "
|
throw new ExternalStorageServiceException("Failed to notify volume state changed "
|
||||||
+ "for vol : " + vol, e);
|
+ "for vol : " + vol, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setResultLocked(CountDownLatch latch, Bundle result) {
|
private void setResult(Bundle result, CompletableFuture<Void> future) {
|
||||||
mLastException = result.getParcelable(EXTRA_ERROR);
|
ParcelableException ex = result.getParcelable(EXTRA_ERROR);
|
||||||
latch.countDown();
|
if (ex != null) {
|
||||||
}
|
future.completeExceptionally(ex);
|
||||||
|
} else {
|
||||||
private void maybeThrowExceptionLocked() throws IOException {
|
future.complete(null);
|
||||||
if (mLastException != null) {
|
|
||||||
ParcelableException lastException = mLastException;
|
|
||||||
mLastException = null;
|
|
||||||
try {
|
|
||||||
lastException.maybeRethrow(IOException.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
throw new RuntimeException(lastException);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CountDownLatch bind() throws ExternalStorageServiceException {
|
private CompletableFuture<IExternalStorageService> connectIfNeeded() throws
|
||||||
|
ExternalStorageServiceException {
|
||||||
ComponentName name = mSessionController.getExternalStorageServiceComponentName();
|
ComponentName name = mSessionController.getExternalStorageServiceComponentName();
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
// Not ready to bind
|
// Not ready to bind
|
||||||
throw new ExternalStorageServiceException(
|
throw new ExternalStorageServiceException(
|
||||||
"Not ready to bind to the ExternalStorageService for user " + mUserId);
|
"Not ready to bind to the ExternalStorageService for user " + mUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mRemote != null || mIsConnecting) {
|
if (mRemoteFuture != null) {
|
||||||
// Connected or connecting (bound or binding)
|
return mRemoteFuture;
|
||||||
// Will wait on a latch that will countdown when we connect, unless we are
|
}
|
||||||
// connected and the latch has already countdown, yay!
|
CompletableFuture<IExternalStorageService> future = new CompletableFuture<>();
|
||||||
return mLatch;
|
|
||||||
} // else neither connected nor connecting
|
|
||||||
|
|
||||||
mLatch = new CountDownLatch(1);
|
|
||||||
mIsConnecting = true;
|
|
||||||
mServiceConnection = new ServiceConnection() {
|
mServiceConnection = new ServiceConnection() {
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
@@ -406,16 +362,9 @@ public final class StorageUserConnection {
|
|||||||
|
|
||||||
private void handleConnection(IBinder service) {
|
private void handleConnection(IBinder service) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mIsConnecting) {
|
future.complete(
|
||||||
mRemote = IExternalStorageService.Stub.asInterface(service);
|
IExternalStorageService.Stub.asInterface(service));
|
||||||
mIsConnecting = false;
|
|
||||||
mLatch.countDown();
|
|
||||||
// Separate thread so we don't block the main thead
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Slog.wtf(TAG, "Connection closed to the ExternalStorageService for user "
|
|
||||||
+ mUserId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDisconnection() {
|
private void handleDisconnection() {
|
||||||
@@ -429,32 +378,19 @@ public final class StorageUserConnection {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Slog.i(TAG, "Binding to the ExternalStorageService for user " + mUserId);
|
Slog.i(TAG, "Binding to the ExternalStorageService for user " + mUserId);
|
||||||
if (mIsDemoUser) {
|
// Schedule on a worker thread, because the system server main thread can be
|
||||||
// Schedule on a worker thread for demo user to avoid deadlock
|
// very busy early in boot.
|
||||||
if (mContext.bindServiceAsUser(new Intent().setComponent(name),
|
if (mContext.bindServiceAsUser(new Intent().setComponent(name),
|
||||||
mServiceConnection,
|
mServiceConnection,
|
||||||
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
|
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
|
||||||
mHandlerThread.getThreadHandler(),
|
mHandlerThread.getThreadHandler(),
|
||||||
UserHandle.of(mUserId))) {
|
UserHandle.of(mUserId))) {
|
||||||
Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId);
|
Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId);
|
||||||
return mLatch;
|
mRemoteFuture = future;
|
||||||
} else {
|
return future;
|
||||||
mIsConnecting = false;
|
|
||||||
throw new ExternalStorageServiceException(
|
|
||||||
"Failed to bind to the ExternalStorageService for user " + mUserId);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (mContext.bindServiceAsUser(new Intent().setComponent(name),
|
throw new ExternalStorageServiceException(
|
||||||
mServiceConnection,
|
"Failed to bind to the ExternalStorageService for user " + mUserId);
|
||||||
Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
|
|
||||||
UserHandle.of(mUserId))) {
|
|
||||||
Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId);
|
|
||||||
return mLatch;
|
|
||||||
} else {
|
|
||||||
mIsConnecting = false;
|
|
||||||
throw new ExternalStorageServiceException(
|
|
||||||
"Failed to bind to the ExternalStorageService for user " + mUserId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,10 +412,5 @@ public final class StorageUserConnection {
|
|||||||
return "[SessionId: " + sessionId + ". UpperPath: " + upperPath + ". LowerPath: "
|
return "[SessionId: " + sessionId + ". UpperPath: " + upperPath + ". LowerPath: "
|
||||||
+ lowerPath + "]";
|
+ lowerPath + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
|
||||||
public boolean isInitialisedLocked() {
|
|
||||||
return !TextUtils.isEmpty(upperPath) && !TextUtils.isEmpty(lowerPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user