diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3ffd7c70b40d8..5f3bad6ad1a83 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -303,8 +303,13 @@ interface IActivityManager {
boolean isTopActivityImmersive();
void crashApplication(int uid, int initialPid, in String packageName, int userId,
in String message, boolean force);
- @UnsupportedAppUsage
+ /** @deprecated -- use getProviderMimeTypeAsync */
+ @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives =
+ "Use {@link android.content.ContentResolver#getType} public API instead.")
String getProviderMimeType(in Uri uri, int userId);
+
+ oneway void getProviderMimeTypeAsync(in Uri uri, int userId, in RemoteCallback resultCallback);
+
// Cause the specified process to dump the specified heap.
boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo,
boolean runGc, in String path, in ParcelFileDescriptor fd,
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index c271e3c66adc8..e1942da8ac7fe 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -49,6 +49,7 @@ import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -299,6 +300,13 @@ 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));
+ callback.sendResult(result);
+ }
+
@Override
public Uri insert(String callingPkg, @Nullable String featureId, Uri uri,
ContentValues initialValues, Bundle extras) {
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 45ace4010961c..0f1442d864ba9 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -33,6 +33,7 @@ import android.os.ICancellationSignal;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import java.io.FileNotFoundException;
@@ -146,6 +147,14 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return true;
}
+ case GET_TYPE_ASYNC_TRANSACTION: {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+ getTypeAsync(url, callback);
+ return true;
+ }
+
case INSERT_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -494,6 +503,22 @@ final class ContentProviderProxy implements IContentProvider
}
}
+ @Override
+ /* oneway */ public void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ try {
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ uri.writeToParcel(data, 0);
+ callback.writeToParcel(data, 0);
+
+ mRemote.transact(IContentProvider.GET_TYPE_ASYNC_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ }
+ }
+
@Override
public Uri insert(String callingPkg, @Nullable String featureId, Uri url,
ContentValues values, Bundle extras) throws RemoteException
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 6cd1cd3354c24..f32a4ab433575 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -55,6 +55,7 @@ import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -67,6 +68,7 @@ import android.util.Log;
import android.util.Size;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.MimeIconUtils;
import dalvik.system.CloseGuard;
@@ -695,6 +697,9 @@ public abstract class ContentResolver implements ContentInterface {
private static final int SLOW_THRESHOLD_MILLIS = 500;
private final Random mRandom = new Random(); // guarded by itself
+ /** @hide */
+ public static final String REMOTE_CALLBACK_RESULT = "result";
+
public ContentResolver(@Nullable Context context) {
this(context, null);
}
@@ -807,7 +812,10 @@ public abstract class ContentResolver implements ContentInterface {
IContentProvider provider = acquireExistingProvider(url);
if (provider != null) {
try {
- return provider.getType(url);
+ final GetTypeResultListener resultListener = new GetTypeResultListener();
+ provider.getTypeAsync(url, new RemoteCallback(resultListener));
+ resultListener.waitForResult();
+ return resultListener.type;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -825,17 +833,53 @@ public abstract class ContentResolver implements ContentInterface {
}
try {
- String type = ActivityManager.getService().getProviderMimeType(
- ContentProvider.getUriWithoutUserId(url), resolveUserId(url));
- return type;
+ GetTypeResultListener resultListener = new GetTypeResultListener();
+ ActivityManager.getService().getProviderMimeTypeAsync(
+ ContentProvider.getUriWithoutUserId(url),
+ resolveUserId(url),
+ new RemoteCallback(resultListener));
+ resultListener.waitForResult();
+ return resultListener.type;
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ // We just failed to send a oneway request to the System Server. Nothing to do.
+ return null;
} catch (java.lang.Exception e) {
Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
return null;
}
}
+ private static final int GET_TYPE_TIMEOUT_MILLIS = 3000;
+
+ private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
+ @GuardedBy("this")
+ public boolean done;
+
+ @GuardedBy("this")
+ public String type;
+
+ @Override
+ public void onResult(Bundle result) {
+ synchronized (this) {
+ type = result.getString(REMOTE_CALLBACK_RESULT);
+ done = true;
+ notifyAll();
+ }
+ }
+
+ public void waitForResult() {
+ synchronized (this) {
+ if (!done) {
+ try {
+ wait(GET_TYPE_TIMEOUT_MILLIS);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+ }
+
/**
* Query for the possible MIME types for the representations the given
* content URL can be returned when opened as as stream with
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 6f477ff17c671..4658ba109d5f1 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -27,6 +27,7 @@ import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import java.io.FileNotFoundException;
@@ -42,6 +43,14 @@ public interface IContentProvider extends IInterface {
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
+
+ /**
+ * An oneway version of getType. The functionality is exactly the same, except that the
+ * call returns immediately, and the resulting type is returned when available via
+ * a binder callback.
+ */
+ void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException;
+
@Deprecated
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+ "ContentProviderClient#insert(android.net.Uri, android.content.ContentValues)} "
@@ -152,4 +161,5 @@ public interface IContentProvider extends IInterface {
static final int UNCANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 25;
static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26;
static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27;
+ int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index b85a332e9000b..23f9f90248df8 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1315,6 +1315,17 @@
android:process=":RedactingProvider">
+
+
+
+
+
+
{
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ removeContentProviderExternalUnchecked(name, null, safeUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ resultCallback.sendResult(result);
+ }));
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Content provider dead retrieving " + uri, e);
+ resultCallback.sendResult(Bundle.EMPTY);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) {
final String name = uri.getAuthority();
final long ident = Binder.clearCallingIdentity();
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index 85e5916a63df9..f7ec11c794764 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.IContentProvider;
@@ -31,10 +32,12 @@ import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import java.io.FileNotFoundException;
@@ -80,6 +83,11 @@ public class MockContentProvider extends ContentProvider {
return MockContentProvider.this.getType(url);
}
+ @Override
+ public void getTypeAsync(Uri uri, RemoteCallback callback) throws RemoteException {
+ MockContentProvider.this.getTypeAsync(uri, callback);
+ }
+
@Override
public Uri insert(String callingPackage, @Nullable String featureId, Uri url,
ContentValues initialValues, Bundle extras) throws RemoteException {
@@ -212,6 +220,18 @@ public class MockContentProvider extends ContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
+ /**
+ * @hide
+ */
+ @SuppressWarnings("deprecation")
+ public void getTypeAsync(Uri uri, RemoteCallback remoteCallback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+ remoteCallback.sendResult(bundle);
+ });
+ }
+
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("unimplemented mock method");
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 464abfb1a5147..1831bcdf9df7a 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -19,16 +19,19 @@ package android.test.mock;
import android.annotation.Nullable;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.EntityIterator;
import android.content.IContentProvider;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import java.io.FileNotFoundException;
@@ -60,6 +63,16 @@ public class MockIContentProvider implements IContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
+ @Override
+ @SuppressWarnings("deprecation")
+ public void getTypeAsync(Uri uri, RemoteCallback remoteCallback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+ remoteCallback.sendResult(bundle);
+ });
+ }
+
@Override
@SuppressWarnings("unused")
public Uri insert(String callingPackage, @Nullable String featureId, Uri url,