• 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.services;
18 
19 import static android.os.SystemClock.uptimeMillis;
20 
21 import static com.android.documentsui.base.SharedMinimal.DEBUG;
22 
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.net.Uri;
26 import android.os.RemoteException;
27 import android.util.Log;
28 
29 import com.android.documentsui.archives.ArchivesProvider;
30 import com.android.documentsui.base.DocumentInfo;
31 import com.android.documentsui.base.DocumentStack;
32 import com.android.documentsui.base.Features;
33 import com.android.documentsui.base.RootInfo;
34 import com.android.documentsui.base.UserId;
35 import com.android.documentsui.clipping.UrisSupplier;
36 import com.android.documentsui.services.FileOperationService.OpType;
37 
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.util.ArrayList;
41 import java.util.List;
42 
43 /**
44  * Abstract job that resolves all resource URIs into mResolvedDocs. This provides
45  * uniform error handling and reporting on resource resolution failures, as well
46  * as an easy path for sub-classes to recover and continue past partial failures.
47  */
48 public abstract class ResolvedResourcesJob extends Job {
49     private static final String TAG = "ResolvedResourcesJob";
50 
51     // Used in logs.
52     protected final long mStartTime = uptimeMillis();
53 
54     final List<DocumentInfo> mResolvedDocs;
55     final List<Uri> mAcquiredArchivedUris = new ArrayList<>();
56 
ResolvedResourcesJob(Context service, Listener listener, String id, @OpType int opType, DocumentStack destination, UrisSupplier srcs, Features features)57     ResolvedResourcesJob(Context service, Listener listener, String id, @OpType int opType,
58             DocumentStack destination, UrisSupplier srcs, Features features) {
59         super(service, listener, id, opType, destination, srcs, features);
60 
61         assert(srcs.getItemCount() > 0);
62 
63         // Delay the initialization of it to setUp() because it may be IO extensive.
64         mResolvedDocs = new ArrayList<>(srcs.getItemCount());
65     }
66 
setUp()67     boolean setUp() {
68         if (!super.setUp()) {
69             return false;
70         }
71 
72         // Acquire all source archived documents, so they are not gone while copying from.
73         try {
74             Iterable<Uri> uris = mResourceUris.getUris(appContext);
75             for (Uri uri : uris) {
76                 try {
77                     if (ArchivesProvider.AUTHORITY.equals(uri.getAuthority())) {
78                         ArchivesProvider.acquireArchive(getClient(uri), uri);
79                         mAcquiredArchivedUris.add(uri);
80                     }
81                 } catch (RemoteException e) {
82                     Log.e(TAG, "Cannot acquire an archive", e);
83                     return false;
84                 }
85             }
86         } catch (IOException e) {
87             Log.e(TAG, "Cannot read list of target resource URIs", e);
88             return false;
89         }
90 
91         int docsResolved = buildDocumentList();
92         if (!isCanceled() && docsResolved < mResourceUris.getItemCount()) {
93             if (docsResolved == 0) {
94                 Log.e(TAG, "Cannot load any documents. Aborting.");
95                 return false;
96             } else {
97                 Log.e(TAG, "Cannot load some documents");
98             }
99         }
100 
101         return true;
102     }
103 
104     @Override
finish()105     void finish() {
106         // Release all archived documents.
107         for (Uri uri : mAcquiredArchivedUris) {
108             try {
109                 ArchivesProvider.releaseArchive(getClient(uri), uri);
110             } catch (RemoteException e) {
111                 Log.e(TAG, "Cannot release an archived document", e);
112             }
113         }
114 
115         if (DEBUG) {
116             Log.d(TAG, String.format("%s %s finished after %d ms", getClass().getSimpleName(), id,
117                     uptimeMillis() - mStartTime));
118         }
119     }
120 
121     /**
122      * Allows sub-classes to exclude files from processing.
123      * By default all files are eligible.
124      */
isEligibleDoc(DocumentInfo doc, RootInfo root)125     boolean isEligibleDoc(DocumentInfo doc, RootInfo root) {
126         return true;
127     }
128 
129     /**
130      * @return number of docs successfully loaded.
131      */
buildDocumentList()132     protected int buildDocumentList() {
133         final ContentResolver resolver = appContext.getContentResolver();
134         Iterable<Uri> uris;
135         try {
136             uris = mResourceUris.getUris(appContext);
137         } catch (IOException e) {
138             Log.e(TAG, "Cannot read list of target resource URIs", e);
139             failureCount = this.mResourceUris.getItemCount();
140             return 0;
141         }
142 
143         int docsLoaded = 0;
144         for (Uri uri : uris) {
145 
146             DocumentInfo doc;
147             try {
148                 doc = DocumentInfo.fromUri(resolver, uri, UserId.DEFAULT_USER);
149             } catch (FileNotFoundException e) {
150                 Log.e(TAG, "Cannot resolve content from URI " + uri, e);
151                 onResolveFailed(uri);
152                 continue;
153             }
154 
155             if (isEligibleDoc(doc, stack.getRoot())) {
156                 mResolvedDocs.add(doc);
157             } else {
158                 onFileFailed(doc);
159             }
160             docsLoaded++;
161 
162             if (isCanceled()) {
163                 break;
164             }
165         }
166 
167         return docsLoaded;
168     }
169 }
170