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

Commit 17dab1c1 authored by Ben Lin's avatar Ben Lin Committed by android-build-merger
Browse files

Crash fix for accessing DocumentInfo.derivedUri when in Recents.

am: e9abd2d4

Change-Id: I0ccdb16db80b11487637b14819af5d187e152f5f
parents 367b3de6 e9abd2d4
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.documentsui.sidebar.EjectRootTask;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Provides support for specializing the actions (viewDocument etc.) to the host activity.
@@ -58,6 +59,7 @@ public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
        implements ActionHandler {

    private static final String TAG = "AbstractActionHandler";
    private static final int REFRESH_SPINNER_TIMEOUT = 500;

    protected final T mActivity;
    protected final State mState;
@@ -106,6 +108,14 @@ public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
                listener).executeOnExecutor(ProviderExecutor.forAuthority(root.authority));
    }

    @Override
    public void refreshDocument(DocumentInfo doc, BooleanConsumer callback) {
        RefreshTask task = new RefreshTask(mState, doc, REFRESH_SPINNER_TIMEOUT,
                mActivity.getApplicationContext(), mActivity::isDestroyed,
                callback);
        task.executeOnExecutor(mExecutors.lookup(doc == null ? null : doc.authority));
    }

    @Override
    public void openSelectedInNewWindow() {
        throw new UnsupportedOperationException("Can't open in new window.");
+6 −0
Original line number Diff line number Diff line
@@ -42,6 +42,12 @@ public interface ActionHandler {
     */
    void ejectRoot(RootInfo root, BooleanConsumer listener);

    /**
     * Attempts to refresh the given DocumentInfo, which should be at the top of the state stack.
     * Returns a boolean answer to the callback, given by {@link ContentProvider#refresh}.
     */
    void refreshDocument(DocumentInfo doc, BooleanConsumer callback);

    void showAppDetails(ResolveInfo info);

    void openRoot(RootInfo root);
+19 −14
Original line number Diff line number Diff line
@@ -29,12 +29,12 @@ import android.os.CancellationSignal;
import android.util.Log;

import com.android.documentsui.base.ApplicationScope;
import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.CheckedTask;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;

import java.util.function.Consumer;

/**
 * A {@link CheckedTask} that calls
 * {@link ContentResolver#refresh(Uri, android.os.Bundle, android.os.CancellationSignal)} on the
@@ -46,14 +46,14 @@ public class RefreshTask extends TimeoutTask<Void, Boolean> {

    private final @ApplicationScope Context mContext;
    private final State mState;
    private final Uri mUri;
    private final Consumer<Boolean> mCallback;
    private final DocumentInfo mDoc;
    private final BooleanConsumer mCallback;
    private final CancellationSignal mSignal;

    public RefreshTask(State state, Uri uri, long timeout, @ApplicationScope Context context, Check check,
            Consumer<Boolean> callback) {
    public RefreshTask(State state, DocumentInfo doc, long timeout,
            @ApplicationScope Context context, Check check, BooleanConsumer callback) {
        super(check);
        mUri = uri;
        mDoc = doc;
        mContext = context;
        mState = state;
        mCallback = callback;
@@ -63,13 +63,18 @@ public class RefreshTask extends TimeoutTask<Void, Boolean> {

    @Override
    public @Nullable Boolean run(Void... params) {
        if (mUri == null) {
            Log.w(TAG, "Attempted to refresh on a null uri. Aborting.");
        if (mDoc == null) {
            Log.w(TAG, "Ignoring attempt to refresh due to null DocumentInfo.");
            return false;
        }

        if (mState.stack.isEmpty()) {
            Log.w(TAG, "Ignoring attempt to refresh due to empty stack.");
            return false;
        }

        if (mUri != mState.stack.peek().derivedUri) {
            Log.w(TAG, "Attempted to refresh on a non-top-level uri. Aborting.");
        if (!mDoc.derivedUri.equals(mState.stack.peek().derivedUri)) {
            Log.w(TAG, "Ignoring attempt to refresh on a non-top-level uri.");
            return false;
        }

@@ -78,17 +83,17 @@ public class RefreshTask extends TimeoutTask<Void, Boolean> {
        // and we will update accordingly. Else, we just tell the callback that Refresh is not
        // supported.
        if (!Shared.ENABLE_OMC_API_FEATURES) {
            Log.w(TAG, "Attempted to call Refresh on an older Android platform. Aborting.");
            Log.w(TAG, "Ignoring attempt to call Refresh on an older Android platform.");
            return false;
        }

        final ContentResolver resolver = mContext.getContentResolver();
        final String authority = mUri.getAuthority();
        final String authority = mDoc.authority;
        boolean refreshSupported = false;
        ContentProviderClient client = null;
        try {
            client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
            refreshSupported = client.refresh(mUri, null, mSignal);
            refreshSupported = client.refresh(mDoc.derivedUri, null, mSignal);
        } catch (Exception e) {
            Log.w(TAG, "Failed to refresh", e);
        } finally {
+4 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.documentsui;
import android.annotation.CallSuper;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;

import com.android.documentsui.base.CheckedTask;
import com.android.documentsui.base.DocumentInfo;
@@ -48,7 +49,9 @@ public abstract class TimeoutTask<Input, Output> extends CheckedTask<Input, Outp
            return;
        }

        Handler handler = new Handler();
        // Need to initialize handler to main Looper so it can initialize correctly in test cases
        // Instrumentation threads don't have looper initialized
        Handler handler = new Handler(Looper.getMainLooper());
        handler.postDelayed(() -> {
            if (getStatus() == AsyncTask.Status.RUNNING) {
                onTimeout();
+9 −12
Original line number Diff line number Diff line
@@ -1145,10 +1145,8 @@ public class DirectoryFragment extends Fragment
            cache.removeUri(mModel.getItemUri(ids[i]));
        }

        final Uri uri = mState.stack.peek().derivedUri;
        RefreshTask task = new RefreshTask(mState, uri, REFRESH_SPINNER_TIMEOUT,
                getContext().getApplicationContext(), this::isDetached,
                (Boolean refreshSupported) -> {
        final DocumentInfo doc = mState.stack.peek();
        mActions.refreshDocument(doc, (boolean refreshSupported) -> {
            if (refreshSupported) {
                mRefreshLayout.setRefreshing(false);
            } else {
@@ -1156,7 +1154,6 @@ public class DirectoryFragment extends Fragment
                getLoaderManager().restartLoader(LOADER_ID, null, mLoaderCallbacks);
            }
        });
        task.executeOnExecutor(mActivity.getExecutorForCurrentDirectory());
    }

    private final class ModelUpdateListener implements EventListener<Model.Update> {
Loading