Merge "Wait for idmap service on current thread" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-04-06 18:49:23 +00:00
committed by Android (Google) Code Review

View File

@@ -18,20 +18,19 @@ package com.android.server.om;
import static android.content.Context.IDMAP_SERVICE; import static android.content.Context.IDMAP_SERVICE;
import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG; import static com.android.server.om.OverlayManagerService.TAG;
import android.os.IBinder; import android.os.IBinder;
import android.os.IIdmap2; import android.os.IIdmap2;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemProperties; import android.os.SystemClock;
import android.os.SystemService;
import android.util.Slog; import android.util.Slog;
import com.android.server.FgThread; import com.android.server.FgThread;
import java.util.concurrent.FutureTask; import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
@@ -45,13 +44,14 @@ class IdmapDaemon {
// The amount of time in milliseconds to wait when attempting to connect to idmap service. // The amount of time in milliseconds to wait when attempting to connect to idmap service.
private static final int SERVICE_CONNECT_TIMEOUT_MS = 5000; private static final int SERVICE_CONNECT_TIMEOUT_MS = 5000;
private static final int SERVICE_CONNECT_INTERVAL_SLEEP_MS = 200;
private static final Object IDMAP_TOKEN = new Object();
private static final String IDMAP_DAEMON = "idmap2d"; private static final String IDMAP_DAEMON = "idmap2d";
private static IdmapDaemon sInstance; private static IdmapDaemon sInstance;
private volatile IIdmap2 mService; private volatile IIdmap2 mService;
private final AtomicInteger mOpenedCount = new AtomicInteger(); private final AtomicInteger mOpenedCount = new AtomicInteger();
private final Object mIdmapToken = new Object();
/** /**
* An {@link AutoCloseable} connection to the idmap service. When the connection is closed or * An {@link AutoCloseable} connection to the idmap service. When the connection is closed or
@@ -62,14 +62,14 @@ class IdmapDaemon {
private boolean mOpened = true; private boolean mOpened = true;
private Connection() { private Connection() {
synchronized (IDMAP_TOKEN) { synchronized (mIdmapToken) {
mOpenedCount.incrementAndGet(); mOpenedCount.incrementAndGet();
} }
} }
@Override @Override
public void close() { public void close() {
synchronized (IDMAP_TOKEN) { synchronized (mIdmapToken) {
if (!mOpened) { if (!mOpened) {
return; return;
} }
@@ -82,7 +82,7 @@ class IdmapDaemon {
} }
FgThread.getHandler().postDelayed(() -> { FgThread.getHandler().postDelayed(() -> {
synchronized (IDMAP_TOKEN) { synchronized (mIdmapToken) {
// Only stop the service if the service does not have an open connection. // Only stop the service if the service does not have an open connection.
if (mService == null || mOpenedCount.get() != 0) { if (mService == null || mOpenedCount.get() != 0) {
return; return;
@@ -91,7 +91,7 @@ class IdmapDaemon {
stopIdmapService(); stopIdmapService();
mService = null; mService = null;
} }
}, IDMAP_TOKEN, SERVICE_TIMEOUT_MS); }, mIdmapToken, SERVICE_TIMEOUT_MS);
} }
} }
} }
@@ -104,14 +104,14 @@ class IdmapDaemon {
} }
String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce, String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
int userId) throws Exception { int userId) throws TimeoutException, RemoteException {
try (Connection connection = connect()) { try (Connection c = connect()) {
return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId); return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId);
} }
} }
boolean removeIdmap(String overlayPath, int userId) throws Exception { boolean removeIdmap(String overlayPath, int userId) throws TimeoutException, RemoteException {
try (Connection connection = connect()) { try (Connection c = connect()) {
return mService.removeIdmap(overlayPath, userId); return mService.removeIdmap(overlayPath, userId);
} }
} }
@@ -119,76 +119,54 @@ class IdmapDaemon {
boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce, boolean verifyIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
int userId) int userId)
throws Exception { throws Exception {
try (Connection connection = connect()) { try (Connection c = connect()) {
return mService.verifyIdmap(targetPath, overlayPath, policies, enforce, userId); return mService.verifyIdmap(targetPath, overlayPath, policies, enforce, userId);
} }
} }
String getIdmapPath(String overlayPath, int userId) throws Exception { String getIdmapPath(String overlayPath, int userId) throws TimeoutException, RemoteException {
try (Connection connection = connect()) { try (Connection c = connect()) {
return mService.getIdmapPath(overlayPath, userId); return mService.getIdmapPath(overlayPath, userId);
} }
} }
private static void startIdmapService() { private IBinder getIdmapService() throws TimeoutException, RemoteException {
SystemProperties.set("ctl.start", IDMAP_DAEMON); SystemService.start(IDMAP_DAEMON);
final long endMillis = SystemClock.elapsedRealtime() + SERVICE_CONNECT_TIMEOUT_MS;
while (SystemClock.elapsedRealtime() <= endMillis) {
final IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
if (binder != null) {
binder.linkToDeath(
() -> Slog.w(TAG, String.format("service '%s' died", IDMAP_SERVICE)), 0);
return binder;
}
try {
Thread.sleep(SERVICE_CONNECT_INTERVAL_SLEEP_MS);
} catch (InterruptedException ignored) {
}
}
throw new TimeoutException(
String.format("Failed to connect to '%s' in %d milliseconds", IDMAP_SERVICE,
SERVICE_CONNECT_TIMEOUT_MS));
} }
private static void stopIdmapService() { private static void stopIdmapService() {
SystemProperties.set("ctl.stop", IDMAP_DAEMON); SystemService.stop(IDMAP_DAEMON);
} }
private Connection connect() throws Exception { private Connection connect() throws TimeoutException, RemoteException {
synchronized (IDMAP_TOKEN) { synchronized (mIdmapToken) {
FgThread.getHandler().removeCallbacksAndMessages(IDMAP_TOKEN); FgThread.getHandler().removeCallbacksAndMessages(mIdmapToken);
if (mService != null) { if (mService != null) {
// Not enough time has passed to stop the idmap service. Reuse the existing // Not enough time has passed to stop the idmap service. Reuse the existing
// interface. // interface.
return new Connection(); return new Connection();
} }
// Start the idmap service if it is not currently running. mService = IIdmap2.Stub.asInterface(getIdmapService());
startIdmapService();
// Block until the service is found.
FutureTask<IBinder> bindIdmap = new FutureTask<>(() -> {
while (true) {
try {
IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
if (binder != null) {
return binder;
}
} catch (Exception e) {
Slog.e(TAG, "service '" + IDMAP_SERVICE + "' not retrieved; "
+ e.getMessage());
}
Thread.sleep(100);
}
});
IBinder binder;
try {
FgThread.getHandler().postAtFrontOfQueue(bindIdmap);
binder = bindIdmap.get(SERVICE_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (Exception rethrow) {
Slog.e(TAG, "service '" + IDMAP_SERVICE + "' not found;");
throw rethrow;
}
try {
binder.linkToDeath(() -> {
Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died");
}, 0);
} catch (RemoteException rethrow) {
Slog.e(TAG, "service '" + IDMAP_SERVICE + "' failed to be bound");
throw rethrow;
}
mService = IIdmap2.Stub.asInterface(binder);
if (DEBUG) {
Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
}
return new Connection(); return new Connection();
} }
} }