Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7a223fbc authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Add async version of "canonicalize"

Fixes: b/147699082
Test: atest FrameworksCoreTests:android.content.ContentResolverTest
Change-Id: I2e851839a454ad5eabc981c76774d03b57a1aa09
parent 80fedffc
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -579,6 +579,15 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
            }
        }

        @Override
        public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
                RemoteCallback callback) {
            final Bundle result = new Bundle();
            result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
                    canonicalize(callingPkg, featureId, uri));
            callback.sendResult(result);
        }

        @Override
        public Uri uncanonicalize(String callingPkg, String featureId,  Uri uri) {
            uri = validateIncomingUri(uri);
+29 −0
Original line number Diff line number Diff line
@@ -359,6 +359,16 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
                    return true;
                }

                case CANONICALIZE_ASYNC_TRANSACTION: {
                    data.enforceInterface(IContentProvider.descriptor);
                    String callingPkg = data.readString();
                    String featureId = data.readString();
                    Uri uri = Uri.CREATOR.createFromParcel(data);
                    RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
                    canonicalizeAsync(callingPkg, featureId, uri, callback);
                    return true;
                }

                case UNCANONICALIZE_TRANSACTION:
                {
                    data.enforceInterface(IContentProvider.descriptor);
@@ -822,6 +832,25 @@ final class ContentProviderProxy implements IContentProvider
        }
    }

    @Override
    /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
            Uri uri, RemoteCallback callback) throws RemoteException {
        Parcel data = Parcel.obtain();
        try {
            data.writeInterfaceToken(IContentProvider.descriptor);

            data.writeString(callingPkg);
            data.writeString(featureId);
            uri.writeToParcel(data, 0);
            callback.writeToParcel(data, 0);

            mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null,
                    Binder.FLAG_ONEWAY);
        } finally {
            data.recycle();
        }
    }

    @Override
    public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
            throws RemoteException {
+38 −15
Original line number Diff line number Diff line
@@ -712,14 +712,17 @@ public abstract class ContentResolver implements ContentInterface {
     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
     * @hide
     */
    public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
    public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS =
            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;

    // Timeout given a ContentProvider that has already been started and connected to.
    private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000;

    // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
    // long ActivityManagerService is giving a content provider to get published if a new process
    // needs to be started for that.
    private static final int GET_TYPE_TIMEOUT_MILLIS =
            CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
    private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS =
            CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS;

    public ContentResolver(@Nullable Context context) {
        this(context, null);
@@ -833,10 +836,10 @@ public abstract class ContentResolver implements ContentInterface {
        IContentProvider provider = acquireExistingProvider(url);
        if (provider != null) {
            try {
                final GetTypeResultListener resultListener = new GetTypeResultListener();
                final StringResultListener resultListener = new StringResultListener();
                provider.getTypeAsync(url, new RemoteCallback(resultListener));
                resultListener.waitForResult();
                return resultListener.type;
                resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
                return resultListener.result;
            } catch (RemoteException e) {
                // Arbitrary and not worth documenting, as Activity
                // Manager will kill this process shortly anyway.
@@ -854,13 +857,13 @@ public abstract class ContentResolver implements ContentInterface {
        }

        try {
            GetTypeResultListener resultListener = new GetTypeResultListener();
            final StringResultListener resultListener = new StringResultListener();
            ActivityManager.getService().getProviderMimeTypeAsync(
                    ContentProvider.getUriWithoutUserId(url),
                    resolveUserId(url),
                    new RemoteCallback(resultListener));
            resultListener.waitForResult();
            return resultListener.type;
            resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
            return resultListener.result;
        } catch (RemoteException e) {
            // We just failed to send a oneway request to the System Server. Nothing to do.
            return null;
@@ -870,27 +873,29 @@ public abstract class ContentResolver implements ContentInterface {
        }
    }

    private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
    private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener {
        @GuardedBy("this")
        public boolean done;

        @GuardedBy("this")
        public String type;
        public T result;

        @Override
        public void onResult(Bundle result) {
            synchronized (this) {
                type = result.getString(REMOTE_CALLBACK_RESULT);
                this.result = getResultFromBundle(result);
                done = true;
                notifyAll();
            }
        }

        public void waitForResult() {
        protected abstract T getResultFromBundle(Bundle result);

        public void waitForResult(long timeout) {
            synchronized (this) {
                if (!done) {
                    try {
                        wait(GET_TYPE_TIMEOUT_MILLIS);
                        wait(timeout);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
@@ -899,6 +904,20 @@ public abstract class ContentResolver implements ContentInterface {
        }
    }

    private static class StringResultListener extends ResultListener<String> {
        @Override
        protected String getResultFromBundle(Bundle result) {
            return result.getString(REMOTE_CALLBACK_RESULT);
        }
    }

    private static class UriResultListener extends ResultListener<Uri> {
        @Override
        protected Uri getResultFromBundle(Bundle result) {
            return result.getParcelable(REMOTE_CALLBACK_RESULT);
        }
    }

    /**
     * Query for the possible MIME types for the representations the given
     * content URL can be returned when opened as as stream with
@@ -1192,7 +1211,11 @@ public abstract class ContentResolver implements ContentInterface {
        }

        try {
            return provider.canonicalize(mPackageName, mFeatureId, url);
            final UriResultListener resultListener = new UriResultListener();
            provider.canonicalizeAsync(mPackageName, mFeatureId, url,
                    new RemoteCallback(resultListener));
            resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
            return resultListener.result;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
+10 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ public interface IContentProvider extends IInterface {
    public String getType(Uri url) throws RemoteException;

    /**
     * An oneway version of getType. The functionality is exactly the same, except that the
     * A 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.
     */
@@ -126,6 +126,14 @@ public interface IContentProvider extends IInterface {
    public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
            throws RemoteException;

    /**
     * A oneway version of canonicalize. 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 canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
            RemoteCallback callback) throws RemoteException;

    public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
            throws RemoteException;

@@ -162,4 +170,5 @@ public interface IContentProvider extends IInterface {
    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;
    int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
}
+8 −0
Original line number Diff line number Diff line
@@ -234,4 +234,12 @@ public class ContentResolverTest {
        assertThat(type).isNull();
        assertThat(end).isLessThan(start + 5000);
    }

    @Test
    public void testCanonicalize() {
        Uri canonical = mResolver.canonicalize(
                Uri.parse("content://android.content.FakeProviderRemote/something"));
        assertThat(canonical).isEqualTo(
                Uri.parse("content://android.content.FakeProviderRemote/canonical"));
    }
}
Loading