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

Commit d2db36ca authored by Kelvin Kwan's avatar Kelvin Kwan Committed by Android (Google) Code Review
Browse files

Merge "Retry and reload until provider is ready after Work Profile is turned on" into rvc-dev

parents 2e49635c e6378169
Loading
Loading
Loading
Loading
+90 −3
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static com.android.documentsui.base.State.MODE_LIST;

import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -31,6 +32,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.DocumentsContract;
@@ -136,6 +138,8 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On

    private static final int CACHE_EVICT_LIMIT = 100;
    private static final int REFRESH_SPINNER_TIMEOUT = 500;
    private static final int PROVIDER_MAX_RETRIES = 10;
    private static final long PROVIDER_TEST_DELAY = 4000;

    private BaseActivity mActivity;

@@ -190,6 +194,9 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On

    private DirectoryState mLocalState;

    private Handler mHandler;
    private Runnable mProviderTestRunnable;

    // Note, we use !null to indicate that selection was restored (from rotation).
    // So don't fiddle with this field unless you've got the bigger picture in mind.
    private @Nullable Bundle mRestoredState;
@@ -225,15 +232,91 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
            final String action = intent.getAction();
            if (isManagedProfileAction(action)) {
                UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER);
                if (Objects.equal(mActivity.getSelectedUser(), UserId.of(userHandle))) {
                UserId userId = UserId.of(userHandle);
                if (Objects.equal(mActivity.getSelectedUser(), userId)) {
                    // We only need to refresh the layout when the selected user is equal to the
                    // received profile user.
                    if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
                        // If the managed profile is turned off, we need to refresh the directory
                        // to update the UI to show an appropriate error message.
                        if (mProviderTestRunnable != null) {
                            mHandler.removeCallbacks(mProviderTestRunnable);
                            mProviderTestRunnable = null;
                        }
                        onRefresh();
                        return;
                    }

                    // When the managed profile becomes available, the provider may not be available
                    // immediately, we need to check if it is ready before we reload the content.
                    if (Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
                        checkUriAndScheduleCheckIfNeeded(userId);
                    }
                }
            }
        }
    };

    private void checkUriAndScheduleCheckIfNeeded(UserId userId) {
        RootInfo currentRoot = mActivity.getCurrentRoot();
        DocumentInfo currentDoc = mActivity.getDisplayState().stack.peek();
        Uri uri = getCurrentUri(currentRoot, currentDoc);
        if (isProviderAvailable(uri, userId) || mActivity.isInRecents()) {
            if (mProviderTestRunnable != null) {
                mHandler.removeCallbacks(mProviderTestRunnable);
                mProviderTestRunnable = null;
            }
            mHandler.post(() -> onRefresh());
        } else {
            checkUriWithDelay(/* numOfRetries= */1, uri, userId);
        }
    }

    private void checkUriWithDelay(int numOfRetries, Uri uri, UserId userId) {
        mProviderTestRunnable = () -> {
            RootInfo currentRoot = mActivity.getCurrentRoot();
            DocumentInfo currentDoc = mActivity.getDisplayState().stack.peek();
            if (mActivity.getSelectedUser().equals(userId)
                    && uri.equals(getCurrentUri(currentRoot, currentDoc))) {
                if (isProviderAvailable(uri, userId)
                        || userId.isQuietModeEnabled(mActivity)
                        || numOfRetries >= PROVIDER_MAX_RETRIES) {
                    // We stop the recursive check when
                    // 1. the provider is available
                    // 2. the profile is in quiet mode, i.e. provider will not be available
                    // 3. after maximum retries
                    onRefresh();
                    mProviderTestRunnable = null;
                } else {
                    Log.d(TAG, "Provider is not available. Retry after " + PROVIDER_TEST_DELAY);
                    checkUriWithDelay(numOfRetries + 1, uri, userId);
                }
            }
        };
        mHandler.postDelayed(mProviderTestRunnable, PROVIDER_TEST_DELAY);
    }

    private Uri getCurrentUri(RootInfo root, @Nullable DocumentInfo doc) {
        String authority = doc == null ? root.authority : doc.authority;
        String documentId = doc == null ? root.documentId : doc.documentId;
        return DocumentsContract.buildDocumentUri(authority, documentId);
    }

    private boolean isProviderAvailable(Uri uri, UserId userId) {
        try (ContentProviderClient userClient =
                     DocumentsApplication.acquireUnstableProviderOrThrow(
                             userId.getContentResolver(mActivity), uri.getAuthority())) {
            Cursor testCursor = userClient.query(uri, /* projection= */ null,
                    /* queryArgs= */null, /* cancellationSignal= */ null);
            if (testCursor != null) {
                return true;
            }
        } catch (Exception e) {
            // Provider is not available. Ignore.
        }
        return false;
    }

    private static boolean isManagedProfileAction(String action) {
        return Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)
                || Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action);
@@ -243,6 +326,7 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mHandler = new Handler(Looper.getMainLooper());
        mActivity = (BaseActivity) getActivity();
        final View view = inflater.inflate(R.layout.fragment_directory, container, false);

@@ -302,6 +386,9 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
        mInjector.actions.unregisterDisplayStateChangedListener(mOnDisplayStateChanged);
        if (mState.supportsCrossProfile()) {
            LocalBroadcastManager.getInstance(mActivity).unregisterReceiver(mReceiver);
            if (mProviderTestRunnable != null) {
                mHandler.removeCallbacks(mProviderTestRunnable);
            }
        }

        // Cancel any outstanding thumbnail requests
@@ -1206,7 +1293,7 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
        }

        final DocumentInfo doc = mActivity.getCurrentDirectory();
        if (doc == null) {
        if (doc == null && !mActivity.getSelectedUser().isQuietModeEnabled(mActivity)) {
            // If there is no root doc, try to reload the root doc from root info.
            Log.w(TAG, "No root document. Try to get root document.");
            getRootDocumentAndMaybeRefreshDocument();