diff options
4 files changed, 72 insertions, 2 deletions
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 65eb642369c9..31c3232f4714 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -47,6 +47,7 @@ import android.os.Bundle; import android.os.CancellationSignal; import android.os.IBinder; import android.os.ICancellationSignal; +import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteCallback; @@ -303,7 +304,23 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall @Override public void getTypeAsync(Uri uri, RemoteCallback callback) { final Bundle result = new Bundle(); - result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri)); + try { + 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); } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 7510ce73a59a..59862ae132a9 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -55,6 +55,7 @@ import android.os.DeadObjectException; import android.os.IBinder; import android.os.ICancellationSignal; import android.os.OperationCanceledException; +import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.RemoteCallback; import android.os.RemoteException; @@ -735,6 +736,9 @@ public abstract class ContentResolver implements ContentInterface { /** @hide */ 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 * before we decide it must be hung. @@ -874,6 +878,9 @@ public abstract class ContentResolver implements ContentInterface { final StringResultListener resultListener = new StringResultListener(); provider.getTypeAsync(url, new RemoteCallback(resultListener)); resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity @@ -898,6 +905,9 @@ public abstract class ContentResolver implements ContentInterface { resolveUserId(url), new RemoteCallback(resultListener)); resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } return resultListener.result; } catch (RemoteException e) { // 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") public T result; + @GuardedBy("this") + public RuntimeException exception; + @Override public void onResult(Bundle result) { synchronized (this) { - this.result = getResultFromBundle(result); + this.exception = getExceptionFromBundle(result); + if (this.exception == null) { + this.result = getResultFromBundle(result); + } done = true; 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); public void waitForResult(long timeout) { @@ -1250,6 +1286,9 @@ public abstract class ContentResolver implements ContentInterface { provider.canonicalizeAsync(mPackageName, mAttributionTag, url, new RemoteCallback(resultListener)); resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS); + if (resultListener.exception != null) { + throw resultListener.exception; + } return resultListener.result; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 1737bd0fa20b..57e5dd8f8f20 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -236,6 +237,16 @@ public class ContentResolverTest { } @Test + public void testGetType_providerException() { + try { + mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error")); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test public void testCanonicalize() { Uri canonical = mResolver.canonicalize( Uri.parse("content://android.content.FakeProviderRemote/something")); diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index 1d7ba5d9be46..a32009493094 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -37,6 +37,9 @@ public class FakeProviderRemote extends ContentProvider { @Override public String getType(Uri uri) { + if (uri.getPath() != null && uri.getPath().contains("error")) { + throw new IllegalArgumentException("Expected exception"); + } return "fake/remote"; } |