1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.documentsui; 18 19 import static com.android.documentsui.base.SharedMinimal.DEBUG; 20 21 import android.content.ContentProviderClient; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.net.Uri; 25 import android.os.CancellationSignal; 26 import android.os.FileUtils; 27 import android.util.Log; 28 29 import androidx.annotation.Nullable; 30 31 import com.android.documentsui.base.ApplicationScope; 32 import com.android.documentsui.base.BooleanConsumer; 33 import com.android.documentsui.base.CheckedTask; 34 import com.android.documentsui.base.DocumentInfo; 35 import com.android.documentsui.base.Features; 36 import com.android.documentsui.base.State; 37 38 /** 39 * A {@link CheckedTask} that calls 40 * {@link ContentResolver#refresh(Uri, android.os.Bundle, android.os.CancellationSignal)} on the 41 * current directory, and then calls the supplied callback with the refresh return value. 42 */ 43 public class RefreshTask extends TimeoutTask<Void, Boolean> { 44 45 private final static String TAG = "RefreshTask"; 46 47 private final @ApplicationScope Context mContext; 48 private final Features mFeatures; 49 private final State mState; 50 private final DocumentInfo mDoc; 51 private final BooleanConsumer mCallback; 52 private final CancellationSignal mSignal; 53 54 RefreshTask(Features features, State state, DocumentInfo doc, long timeout, @ApplicationScope Context context, Check check, BooleanConsumer callback)55 public RefreshTask(Features features, State state, DocumentInfo doc, long timeout, 56 @ApplicationScope Context context, Check check, BooleanConsumer callback) { 57 super(check, timeout); 58 mFeatures = features; 59 mState = state; 60 mDoc = doc; 61 mContext = context; 62 mCallback = callback; 63 mSignal = new CancellationSignal(); 64 } 65 66 @Override run(Void... params)67 public @Nullable Boolean run(Void... params) { 68 if (mDoc == null) { 69 Log.w(TAG, "Ignoring attempt to refresh due to null DocumentInfo."); 70 return false; 71 } 72 73 if (mState.stack.isEmpty()) { 74 Log.w(TAG, "Ignoring attempt to refresh due to empty stack."); 75 return false; 76 } 77 78 if (mDoc.derivedUri == null) { 79 Log.w(TAG, "Ignoring attempt to refresh due to null derived uri in DocumentInfo."); 80 return false; 81 } 82 83 if (!mDoc.derivedUri.equals(mState.stack.peek().derivedUri)) { 84 Log.w(TAG, "Ignoring attempt to refresh on a non-top-level uri."); 85 return false; 86 } 87 88 if (!mState.canInteractWith(mDoc.userId) || mDoc.userId.isQuietModeEnabled(mContext)) { 89 // No result was returned by these errors so it does not support refresh. 90 Log.w(TAG, "Cannot refresh due to cross profile error."); 91 return false; 92 } 93 94 // API O introduces ContentResolver#refresh, and if available and the ContentProvider 95 // supports it, the ContentProvider will automatically send a content updated notification 96 // and we will update accordingly. Else, we just tell the callback that Refresh is not 97 // supported. 98 if (!mFeatures.isContentRefreshEnabled()) { 99 Log.w(TAG, "Ignoring attempt to call Refresh on an older Android platform."); 100 return false; 101 } 102 103 final ContentResolver resolver = mDoc.userId.getContentResolver(mContext); 104 final String authority = mDoc.authority; 105 boolean refreshSupported = false; 106 ContentProviderClient client = null; 107 try { 108 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); 109 refreshSupported = client.refresh(mDoc.derivedUri, null, mSignal); 110 } catch (Exception e) { 111 Log.w(TAG, "Failed to refresh", e); 112 } finally { 113 FileUtils.closeQuietly(client); 114 } 115 return refreshSupported; 116 } 117 118 @Override onTimeout()119 protected void onTimeout() { 120 mSignal.cancel(); 121 Log.w(TAG, "Provider taking too long to respond. Cancelling."); 122 } 123 124 @Override finish(Boolean refreshSupported)125 public void finish(Boolean refreshSupported) { 126 if (DEBUG) { 127 // In case of timeout, refreshSupported is null. 128 if (Boolean.TRUE.equals(refreshSupported)) { 129 Log.v(TAG, "Provider supports refresh and has refreshed"); 130 } else { 131 Log.v(TAG, "Provider does not support refresh and did not refresh"); 132 } 133 } 134 mCallback.accept(refreshSupported != null ? refreshSupported : Boolean.FALSE); 135 } 136 } 137