Merge "Fix potential deadlock in ServiceWatcher"

This commit is contained in:
Soonil Nagarkar
2019-01-26 16:39:28 +00:00
committed by Android (Google) Code Review
3 changed files with 123 additions and 119 deletions

View File

@@ -34,11 +34,11 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.Preconditions;
@@ -48,6 +48,9 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Find the best Service, and bind to it.
@@ -61,16 +64,20 @@ public class ServiceWatcher implements ServiceConnection {
public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
/**
* The runner that runs on the binder retrieved from {@link ServiceWatcher}.
*/
/** Function to run on binder interface. */
public interface BinderRunner {
/**
* Runs on the retrieved binder.
*
* @param binder the binder retrieved from the {@link ServiceWatcher}.
*/
void run(IBinder binder);
/** Called to run client code with the binder. */
void run(IBinder binder) throws RemoteException;
}
/**
* Function to run on binder interface.
* @param <T> Type to return.
*/
public interface BlockingBinderRunner<T> {
/** Called to run client code with the binder. */
T run(IBinder binder) throws RemoteException;
}
public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
@@ -120,18 +127,14 @@ public class ServiceWatcher implements ServiceConnection {
private final Handler mHandler;
// this lock is held to ensure the service binder is not exposed (via runOnBinder) until after
// the new service initialization work has completed
private final Object mBindLock = new Object();
// read/write from handler thread
private IBinder mBestService;
private int mCurrentUserId;
// read from any thread, write from handler thread
private volatile ComponentName mBestComponent;
private volatile int mBestVersion;
private volatile int mBestUserId;
private volatile IBinder mBestService;
public ServiceWatcher(Context context, String logTag, String action,
int overlaySwitchResId, int defaultServicePackageNameResId,
@@ -163,17 +166,9 @@ public class ServiceWatcher implements ServiceConnection {
mBestService = null;
}
// called on handler thread
@GuardedBy("mBindLock")
protected void onBind() {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
protected void onBind() {}
// called on handler thread
@GuardedBy("mBindLock")
protected void onUnbind() {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
}
protected void onUnbind() {}
/**
* Start this watcher, including binding to the current best match and
@@ -248,25 +243,6 @@ public class ServiceWatcher implements ServiceConnection {
return bestComponent == null ? null : bestComponent.getPackageName();
}
/**
* Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
* serially.
*/
public final void runOnBinder(BinderRunner runner) {
synchronized (mBindLock) {
IBinder service = mBestService;
if (service != null) {
try {
runner.run(service);
} catch (Exception e) {
// remote exceptions cannot be allowed to crash system server
Log.e(TAG, "exception while while running " + runner + " on " + service
+ " from " + this, e);
}
}
}
}
private boolean isServiceMissing() {
return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
PackageManager.MATCH_DIRECT_BOOT_AWARE
@@ -380,28 +356,66 @@ public class ServiceWatcher implements ServiceConnection {
mBestUserId = UserHandle.USER_NULL;
}
/**
* Runs the given function asynchronously if currently connected. Suppresses any RemoteException
* thrown during execution.
*/
public final void runOnBinder(BinderRunner runner) {
runOnHandler(() -> {
if (mBestService == null) {
return;
}
try {
runner.run(mBestService);
} catch (RuntimeException e) {
// the code being run is privileged, but may be outside the system server, and thus
// we cannot allow runtime exceptions to crash the system server
Log.e(TAG, "exception while while running " + runner + " on " + mBestService
+ " from " + this, e);
} catch (RemoteException e) {
// do nothing
}
});
}
/**
* Runs the given function synchronously if currently connected, and returns the default value
* if not currently connected or if any exception is thrown.
*/
public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
try {
return runOnHandlerBlocking(() -> {
if (mBestService == null) {
return defaultValue;
}
try {
return runner.run(mBestService);
} catch (RemoteException e) {
return defaultValue;
}
});
} catch (InterruptedException e) {
return defaultValue;
}
}
@Override
public final void onServiceConnected(ComponentName component, IBinder binder) {
mHandler.post(() -> {
runOnHandler(() -> {
if (D) Log.d(mTag, component + " connected");
// hold the lock so that mBestService cannot be used by runOnBinder until complete
synchronized (mBindLock) {
mBestService = binder;
onBind();
}
mBestService = binder;
onBind();
});
}
@Override
public final void onServiceDisconnected(ComponentName component) {
mHandler.post(() -> {
runOnHandler(() -> {
if (D) Log.d(mTag, component + " disconnected");
mBestService = null;
synchronized (mBindLock) {
onUnbind();
}
onUnbind();
});
}
@@ -410,4 +424,32 @@ public class ServiceWatcher implements ServiceConnection {
ComponentName bestComponent = mBestComponent;
return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
}
private void runOnHandler(Runnable r) {
if (Looper.myLooper() == mHandler.getLooper()) {
r.run();
} else {
mHandler.post(r);
}
}
private <T> T runOnHandlerBlocking(Callable<T> c) throws InterruptedException {
if (Looper.myLooper() == mHandler.getLooper()) {
try {
return c.call();
} catch (Exception e) {
// Function cannot throw exception, this should never happen
throw new IllegalStateException(e);
}
} else {
FutureTask<T> task = new FutureTask<>(c);
mHandler.post(task);
try {
return task.get();
} catch (ExecutionException e) {
// Function cannot throw exception, this should never happen
throw new IllegalStateException(e);
}
}
}
}

View File

@@ -20,8 +20,6 @@ import android.content.Context;
import android.location.Address;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.os.BackgroundThread;
import com.android.server.ServiceWatcher;
@@ -68,35 +66,22 @@ public class GeocoderProxy {
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
final String[] result = new String[]{"Service not Available"};
mServiceWatcher.runOnBinder(binder -> {
return mServiceWatcher.runOnBinderBlocking(binder -> {
IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
try {
result[0] = provider.getFromLocation(
latitude, longitude, maxResults, params, addrs);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
return result[0];
return provider.getFromLocation(latitude, longitude, maxResults, params, addrs);
}, "Service not Available");
}
public String getFromLocationName(String locationName,
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
final String[] result = new String[]{"Service not Available"};
mServiceWatcher.runOnBinder(binder -> {
return mServiceWatcher.runOnBinderBlocking(binder -> {
IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
try {
result[0] = provider.getFromLocationName(locationName, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params, addrs);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
return result[0];
return provider.getFromLocationName(locationName, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params, addrs);
}, "Service not Available");
}
}

View File

@@ -127,20 +127,16 @@ public class LocationProviderProxy extends AbstractLocationProvider {
return mServiceWatcher.start();
}
private void initializeService(IBinder binder) {
private void initializeService(IBinder binder) throws RemoteException {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
try {
service.setLocationProviderManager(mManager);
service.setLocationProviderManager(mManager);
synchronized (mRequestLock) {
if (mRequest != null) {
service.setRequest(mRequest, mWorkSource);
}
synchronized (mRequestLock) {
if (mRequest != null) {
service.setRequest(mRequest, mWorkSource);
}
} catch (RemoteException e) {
Log.w(TAG, e);
}
}
@@ -157,63 +153,44 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
service.setRequest(request, source);
} catch (RemoteException e) {
Log.w(TAG, e);
}
service.setRequest(request, source);
});
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(" service=" + mServiceWatcher);
mServiceWatcher.runOnBinder(binder -> {
mServiceWatcher.runOnBinderBlocking(binder -> {
try {
TransferPipe.dumpAsync(binder, fd, args);
} catch (IOException | RemoteException e) {
pw.println(" failed to dump location provider: " + e);
pw.println(" failed to dump location provider");
}
});
return null;
}, null);
}
@Override
public int getStatus(Bundle extras) {
int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
mServiceWatcher.runOnBinder(binder -> {
return mServiceWatcher.runOnBinderBlocking(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
status[0] = service.getStatus(extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
return status[0];
return service.getStatus(extras);
}, LocationProvider.TEMPORARILY_UNAVAILABLE);
}
@Override
public long getStatusUpdateTime() {
long[] updateTime = new long[] {0L};
mServiceWatcher.runOnBinder(binder -> {
return mServiceWatcher.runOnBinderBlocking(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
updateTime[0] = service.getStatusUpdateTime();
} catch (RemoteException e) {
Log.w(TAG, e);
}
});
return updateTime[0];
return service.getStatusUpdateTime();
}, 0L);
}
@Override
public void sendExtraCommand(String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
try {
service.sendExtraCommand(command, extras);
} catch (RemoteException e) {
Log.w(TAG, e);
}
service.sendExtraCommand(command, extras);
});
}
}