Merge "Propagate exception thrown by ContentProvider.getType" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f903ef10e5
@@ -47,6 +47,7 @@ import android.os.Bundle;
|
|||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ICancellationSignal;
|
import android.os.ICancellationSignal;
|
||||||
|
import android.os.Parcel;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteCallback;
|
import android.os.RemoteCallback;
|
||||||
@@ -303,7 +304,23 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
|
|||||||
@Override
|
@Override
|
||||||
public void getTypeAsync(Uri uri, RemoteCallback callback) {
|
public void getTypeAsync(Uri uri, RemoteCallback callback) {
|
||||||
final Bundle result = new Bundle();
|
final Bundle result = new Bundle();
|
||||||
|
try {
|
||||||
result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
|
result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
parcel.writeException(e);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// getType threw an unparcelable exception. Wrap the message into
|
||||||
|
// a parcelable exception type
|
||||||
|
parcel.writeException(new IllegalStateException(e.getMessage()));
|
||||||
|
}
|
||||||
|
result.putByteArray(ContentResolver.REMOTE_CALLBACK_ERROR, parcel.marshall());
|
||||||
|
} finally {
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
callback.sendResult(result);
|
callback.sendResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import android.os.DeadObjectException;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ICancellationSignal;
|
import android.os.ICancellationSignal;
|
||||||
import android.os.OperationCanceledException;
|
import android.os.OperationCanceledException;
|
||||||
|
import android.os.Parcel;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteCallback;
|
import android.os.RemoteCallback;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -735,6 +736,9 @@ public abstract class ContentResolver implements ContentInterface {
|
|||||||
/** @hide */
|
/** @hide */
|
||||||
public static final String REMOTE_CALLBACK_RESULT = "result";
|
public static final String REMOTE_CALLBACK_RESULT = "result";
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public static final String REMOTE_CALLBACK_ERROR = "error";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How long we wait for an attached process to publish its content providers
|
* How long we wait for an attached process to publish its content providers
|
||||||
* before we decide it must be hung.
|
* before we decide it must be hung.
|
||||||
@@ -874,6 +878,9 @@ public abstract class ContentResolver implements ContentInterface {
|
|||||||
final StringResultListener resultListener = new StringResultListener();
|
final StringResultListener resultListener = new StringResultListener();
|
||||||
provider.getTypeAsync(url, new RemoteCallback(resultListener));
|
provider.getTypeAsync(url, new RemoteCallback(resultListener));
|
||||||
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
|
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
|
||||||
|
if (resultListener.exception != null) {
|
||||||
|
throw resultListener.exception;
|
||||||
|
}
|
||||||
return resultListener.result;
|
return resultListener.result;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// Arbitrary and not worth documenting, as Activity
|
// Arbitrary and not worth documenting, as Activity
|
||||||
@@ -898,6 +905,9 @@ public abstract class ContentResolver implements ContentInterface {
|
|||||||
resolveUserId(url),
|
resolveUserId(url),
|
||||||
new RemoteCallback(resultListener));
|
new RemoteCallback(resultListener));
|
||||||
resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
|
resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
|
||||||
|
if (resultListener.exception != null) {
|
||||||
|
throw resultListener.exception;
|
||||||
|
}
|
||||||
return resultListener.result;
|
return resultListener.result;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// We just failed to send a oneway request to the System Server. Nothing to do.
|
// We just failed to send a oneway request to the System Server. Nothing to do.
|
||||||
@@ -915,15 +925,41 @@ public abstract class ContentResolver implements ContentInterface {
|
|||||||
@GuardedBy("this")
|
@GuardedBy("this")
|
||||||
public T result;
|
public T result;
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
public RuntimeException exception;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResult(Bundle result) {
|
public void onResult(Bundle result) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
this.exception = getExceptionFromBundle(result);
|
||||||
|
if (this.exception == null) {
|
||||||
this.result = getResultFromBundle(result);
|
this.result = getResultFromBundle(result);
|
||||||
|
}
|
||||||
done = true;
|
done = true;
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RuntimeException getExceptionFromBundle(Bundle result) {
|
||||||
|
byte[] bytes = result.getByteArray(REMOTE_CALLBACK_ERROR);
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
try {
|
||||||
|
parcel.unmarshall(bytes, 0, bytes.length);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
parcel.readException();
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
return ex;
|
||||||
|
} finally {
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract T getResultFromBundle(Bundle result);
|
protected abstract T getResultFromBundle(Bundle result);
|
||||||
|
|
||||||
public void waitForResult(long timeout) {
|
public void waitForResult(long timeout) {
|
||||||
@@ -1250,6 +1286,9 @@ public abstract class ContentResolver implements ContentInterface {
|
|||||||
provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
|
provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
|
||||||
new RemoteCallback(resultListener));
|
new RemoteCallback(resultListener));
|
||||||
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
|
resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
|
||||||
|
if (resultListener.exception != null) {
|
||||||
|
throw resultListener.exception;
|
||||||
|
}
|
||||||
return resultListener.result;
|
return resultListener.result;
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// Arbitrary and not worth documenting, as Activity
|
// Arbitrary and not worth documenting, as Activity
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -235,6 +236,16 @@ public class ContentResolverTest {
|
|||||||
assertThat(end).isLessThan(start + 5000);
|
assertThat(end).isLessThan(start + 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetType_providerException() {
|
||||||
|
try {
|
||||||
|
mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error"));
|
||||||
|
fail("Expected IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCanonicalize() {
|
public void testCanonicalize() {
|
||||||
Uri canonical = mResolver.canonicalize(
|
Uri canonical = mResolver.canonicalize(
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ public class FakeProviderRemote extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType(Uri uri) {
|
public String getType(Uri uri) {
|
||||||
|
if (uri.getPath() != null && uri.getPath().contains("error")) {
|
||||||
|
throw new IllegalArgumentException("Expected exception");
|
||||||
|
}
|
||||||
return "fake/remote";
|
return "fake/remote";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user