• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
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 package com.google.android.libraries.mobiledatadownload.internal.util;
17 
18 import android.content.Context;
19 import android.net.Uri;
20 import android.text.TextUtils;
21 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
22 import com.google.android.libraries.mobiledatadownload.file.common.LimitExceededException;
23 import com.google.android.libraries.mobiledatadownload.file.common.MalformedUriException;
24 import com.google.android.libraries.mobiledatadownload.file.common.UnsupportedFileStorageOperation;
25 import com.google.android.libraries.mobiledatadownload.file.openers.ReadStreamOpener;
26 import com.google.android.libraries.mobiledatadownload.file.openers.WriteStreamOpener;
27 import com.google.android.libraries.mobiledatadownload.internal.logging.LogUtil;
28 import com.google.common.io.ByteStreams;
29 import com.google.mobiledatadownload.internal.MetadataProto.DataFile;
30 import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 
35 /** Utils for Android file sharing. */
36 public final class AndroidSharingUtil {
37 
38   private static final String TAG = "AndroidSharingUtil";
39 
40   /**
41    * Exception thrown if an eror occurs while trying to share a file with the Android Blob Sharing
42    * Service.
43    */
44   public static final class AndroidSharingException extends Exception {
45     // The error code to be logged.
46     private final int errorCode;
47 
AndroidSharingException(int errorCode, String message)48     public AndroidSharingException(int errorCode, String message) {
49       super(message);
50       this.errorCode = errorCode;
51     }
52 
getErrorCode()53     public int getErrorCode() {
54       return errorCode;
55     }
56   }
57 
AndroidSharingUtil()58   private AndroidSharingUtil() {}
59 
60   /** Returns true if a blob with checksum {@code checksum} already exists in the shared storage. */
blobExists( Context context, String checksum, DataFileGroupInternal fileGroup, DataFile dataFile, SynchronousFileStorage fileStorage)61   public static boolean blobExists(
62       Context context,
63       String checksum,
64       DataFileGroupInternal fileGroup,
65       DataFile dataFile,
66       SynchronousFileStorage fileStorage)
67       throws AndroidSharingException {
68     int errorCode = -1;
69     boolean exists = false;
70     String message = "";
71     try {
72       Uri blobUri = DirectoryUtil.getBlobUri(context, checksum);
73       exists = fileStorage.exists(blobUri);
74     } catch (UnsupportedFileStorageOperation e) {
75       String msg = TextUtils.isEmpty(e.getMessage()) ? "" : e.getMessage();
76       LogUtil.v(
77           "%s: Failed to share for file %s, file group %s."
78               + " UnsupportedFileStorageOperation was thrown with message \"%s\"",
79           TAG, dataFile.getFileId(), fileGroup.getGroupName(), msg);
80       errorCode = 0;
81       message = "UnsupportedFileStorageOperation was thrown: " + msg;
82     } catch (MalformedUriException e) {
83       LogUtil.e(
84           "%s: Malformed lease uri file %s, file group %s",
85           TAG, dataFile.getFileId(), fileGroup.getGroupName());
86       errorCode = 0;
87       message =
88           String.format(
89               "Malformed blob Uri for file %s, group %s",
90               dataFile.getFileId(), fileGroup.getGroupName());
91     } catch (IOException e) {
92       LogUtil.e(
93           "%s: Failed to check existence in the shared storage for file %s, file group" + " %s",
94           TAG, dataFile.getFileId(), fileGroup.getGroupName());
95       errorCode = 0;
96       message =
97           String.format(
98               "Error while checking if file %s, group %s, exists in the shared blob storage.",
99               dataFile.getFileId(), fileGroup.getGroupName());
100     }
101     if (errorCode != -1) {
102       throw new AndroidSharingException(errorCode, message);
103     }
104     return exists;
105   }
106 
107   /**
108    * Copies the local {@code downloadFileOnDeviceUri} to the blob storage.
109    *
110    * @param afterDownload whether this function is called before or after the {@code dataFile}'s
111    *     download.
112    */
copyFileToBlobStore( Context context, String checksum, Uri downloadFileOnDeviceUri, DataFileGroupInternal fileGroup, DataFile dataFile, SynchronousFileStorage fileStorage, boolean afterDownload)113   public static void copyFileToBlobStore(
114       Context context,
115       String checksum,
116       Uri downloadFileOnDeviceUri,
117       DataFileGroupInternal fileGroup,
118       DataFile dataFile,
119       SynchronousFileStorage fileStorage,
120       boolean afterDownload)
121       throws AndroidSharingException {
122     int errorCode = -1;
123     String message = "";
124     try {
125       Uri blobUri = DirectoryUtil.getBlobUri(context, checksum);
126       try (InputStream in = fileStorage.open(downloadFileOnDeviceUri, ReadStreamOpener.create());
127           OutputStream out = fileStorage.open(blobUri, WriteStreamOpener.create())) {
128         ByteStreams.copy(in, out);
129       }
130     } catch (UnsupportedFileStorageOperation e) {
131       String msg = TextUtils.isEmpty(e.getMessage()) ? "" : e.getMessage();
132       LogUtil.v(
133           "%s: Failed to share after download for file %s, file group %s."
134               + " UnsupportedFileStorageOperation was thrown with message \"%s\"",
135           TAG, dataFile.getFileId(), fileGroup.getGroupName(), msg);
136       errorCode = 0;
137       message = "UnsupportedFileStorageOperation was thrown: " + msg;
138     } catch (LimitExceededException e) {
139       LogUtil.e(
140           "%s: Failed to share after download for file %s, file group %s due to"
141               + " LimitExceededException",
142           TAG, dataFile.getFileId(), fileGroup.getGroupName());
143       errorCode = 0;
144       message =
145           String.format(
146               "System limit exceeded for file %s, group %s",
147               dataFile.getFileId(), fileGroup.getGroupName());
148     } catch (MalformedUriException e) {
149       LogUtil.e(
150           "%s: Malformed lease uri file %s, file group %s",
151           TAG, dataFile.getFileId(), fileGroup.getGroupName());
152       errorCode = 0;
153       message =
154           String.format(
155               "Malformed blob Uri for file %s, group %s",
156               dataFile.getFileId(), fileGroup.getGroupName());
157     } catch (IOException e) {
158       LogUtil.e(
159           "%s: Failed to copy to the blobstore after download for file %s, file group" + " %s",
160           TAG, dataFile.getFileId(), fileGroup.getGroupName());
161       errorCode = afterDownload ? 0 : 0;
162       message =
163           String.format(
164               "Error while copying file %s, group %s, to the shared blob storage",
165               dataFile.getFileId(), fileGroup.getGroupName());
166     }
167     if (errorCode != -1) {
168       throw new AndroidSharingException(errorCode, message);
169     }
170   }
171 
172   /** Acquires the lease on the shared {@code dataFile}. */
acquireLease( Context context, String checksum, long expiryDate, DataFileGroupInternal fileGroup, DataFile dataFile, SynchronousFileStorage fileStorage)173   public static void acquireLease(
174       Context context,
175       String checksum,
176       long expiryDate,
177       DataFileGroupInternal fileGroup,
178       DataFile dataFile,
179       SynchronousFileStorage fileStorage)
180       throws AndroidSharingException {
181     int errorCode = -1;
182     String message = "";
183     try {
184       Uri leaseUri = DirectoryUtil.getBlobStoreLeaseUri(context, checksum, expiryDate);
185       // Acquires/updates the lease to the blob.
186       // TODO(b/149260496): catch LimitExceededException, thrown when a lease could not be acquired,
187       // such as when the caller is trying to acquire leases on too much data.
188       try (OutputStream out = fileStorage.open(leaseUri, WriteStreamOpener.create())) {}
189 
190     } catch (UnsupportedFileStorageOperation e) {
191       String msg = TextUtils.isEmpty(e.getMessage()) ? "" : e.getMessage();
192       LogUtil.v(
193           "%s: Failed to share file %s, file group %s."
194               + " UnsupportedFileStorageOperation was thrown with message \"%s\"",
195           TAG, dataFile.getFileId(), fileGroup.getGroupName(), msg);
196       errorCode = 0;
197       message = "UnsupportedFileStorageOperation was thrown: " + msg;
198     } catch (MalformedUriException e) {
199       LogUtil.e(
200           "%s: Malformed lease uri file %s, file group %s",
201           TAG, dataFile.getFileId(), fileGroup.getGroupName());
202       errorCode = 0;
203       message =
204           String.format(
205               "Malformed lease Uri for file %s, group %s",
206               dataFile.getFileId(), fileGroup.getGroupName());
207     } catch (LimitExceededException e) {
208       LogUtil.e(
209           "%s: Failed to share after download for file %s, file group %s due to"
210               + " LimitExceededException",
211           TAG, dataFile.getFileId(), fileGroup.getGroupName());
212       errorCode = 0;
213       message =
214           String.format(
215               "System limit exceeded for file %s, group %s",
216               dataFile.getFileId(), fileGroup.getGroupName());
217     } catch (IOException e) {
218       LogUtil.e(
219           "%s: Failed to acquire lease for file %s, file group" + " %s",
220           TAG, dataFile.getFileId(), fileGroup.getGroupName());
221       errorCode = 0;
222       message =
223           String.format(
224               "Error while acquiring lease for file %s, group %s",
225               dataFile.getFileId(), fileGroup.getGroupName());
226     }
227     if (errorCode != -1) {
228       throw new AndroidSharingException(errorCode, message);
229     }
230   }
231 }
232