• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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