blob: 96ed7dc7c92e0bcd2c6c1a7b609b9e36c029f298 [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.libraries.mobiledatadownload.internal.util;
import android.content.Context;
import android.net.Uri;
import android.text.TextUtils;
import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage;
import com.google.android.libraries.mobiledatadownload.file.common.LimitExceededException;
import com.google.android.libraries.mobiledatadownload.file.common.MalformedUriException;
import com.google.android.libraries.mobiledatadownload.file.common.UnsupportedFileStorageOperation;
import com.google.android.libraries.mobiledatadownload.file.openers.ReadStreamOpener;
import com.google.android.libraries.mobiledatadownload.file.openers.WriteStreamOpener;
import com.google.android.libraries.mobiledatadownload.internal.logging.LogUtil;
import com.google.common.io.ByteStreams;
import com.google.mobiledatadownload.internal.MetadataProto.DataFile;
import com.google.mobiledatadownload.internal.MetadataProto.DataFileGroupInternal;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/** Utils for Android file sharing. */
public final class AndroidSharingUtil {
private static final String TAG = "AndroidSharingUtil";
/**
* Exception thrown if an eror occurs while trying to share a file with the Android Blob Sharing
* Service.
*/
public static final class AndroidSharingException extends Exception {
// The error code to be logged.
private final int errorCode;
public AndroidSharingException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
}
private AndroidSharingUtil() {}
/** Returns true if a blob with checksum {@code checksum} already exists in the shared storage. */
public static boolean blobExists(
Context context,
String checksum,
DataFileGroupInternal fileGroup,
DataFile dataFile,
SynchronousFileStorage fileStorage)
throws AndroidSharingException {
int errorCode = -1;
boolean exists = false;
String message = "";
try {
Uri blobUri = DirectoryUtil.getBlobUri(context, checksum);
exists = fileStorage.exists(blobUri);
} catch (UnsupportedFileStorageOperation e) {
String msg = TextUtils.isEmpty(e.getMessage()) ? "" : e.getMessage();
LogUtil.v(
"%s: Failed to share for file %s, file group %s."
+ " UnsupportedFileStorageOperation was thrown with message \"%s\"",
TAG, dataFile.getFileId(), fileGroup.getGroupName(), msg);
errorCode = 0;
message = "UnsupportedFileStorageOperation was thrown: " + msg;
} catch (MalformedUriException e) {
LogUtil.e(
"%s: Malformed lease uri file %s, file group %s",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"Malformed blob Uri for file %s, group %s",
dataFile.getFileId(), fileGroup.getGroupName());
} catch (IOException e) {
LogUtil.e(
"%s: Failed to check existence in the shared storage for file %s, file group" + " %s",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"Error while checking if file %s, group %s, exists in the shared blob storage.",
dataFile.getFileId(), fileGroup.getGroupName());
}
if (errorCode != -1) {
throw new AndroidSharingException(errorCode, message);
}
return exists;
}
/**
* Copies the local {@code downloadFileOnDeviceUri} to the blob storage.
*
* @param afterDownload whether this function is called before or after the {@code dataFile}'s
* download.
*/
public static void copyFileToBlobStore(
Context context,
String checksum,
Uri downloadFileOnDeviceUri,
DataFileGroupInternal fileGroup,
DataFile dataFile,
SynchronousFileStorage fileStorage,
boolean afterDownload)
throws AndroidSharingException {
int errorCode = -1;
String message = "";
try {
Uri blobUri = DirectoryUtil.getBlobUri(context, checksum);
try (InputStream in = fileStorage.open(downloadFileOnDeviceUri, ReadStreamOpener.create());
OutputStream out = fileStorage.open(blobUri, WriteStreamOpener.create())) {
ByteStreams.copy(in, out);
}
} catch (UnsupportedFileStorageOperation e) {
String msg = TextUtils.isEmpty(e.getMessage()) ? "" : e.getMessage();
LogUtil.v(
"%s: Failed to share after download for file %s, file group %s."
+ " UnsupportedFileStorageOperation was thrown with message \"%s\"",
TAG, dataFile.getFileId(), fileGroup.getGroupName(), msg);
errorCode = 0;
message = "UnsupportedFileStorageOperation was thrown: " + msg;
} catch (LimitExceededException e) {
LogUtil.e(
"%s: Failed to share after download for file %s, file group %s due to"
+ " LimitExceededException",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"System limit exceeded for file %s, group %s",
dataFile.getFileId(), fileGroup.getGroupName());
} catch (MalformedUriException e) {
LogUtil.e(
"%s: Malformed lease uri file %s, file group %s",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"Malformed blob Uri for file %s, group %s",
dataFile.getFileId(), fileGroup.getGroupName());
} catch (IOException e) {
LogUtil.e(
"%s: Failed to copy to the blobstore after download for file %s, file group" + " %s",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = afterDownload ? 0 : 0;
message =
String.format(
"Error while copying file %s, group %s, to the shared blob storage",
dataFile.getFileId(), fileGroup.getGroupName());
}
if (errorCode != -1) {
throw new AndroidSharingException(errorCode, message);
}
}
/** Acquires the lease on the shared {@code dataFile}. */
public static void acquireLease(
Context context,
String checksum,
long expiryDate,
DataFileGroupInternal fileGroup,
DataFile dataFile,
SynchronousFileStorage fileStorage)
throws AndroidSharingException {
int errorCode = -1;
String message = "";
try {
Uri leaseUri = DirectoryUtil.getBlobStoreLeaseUri(context, checksum, expiryDate);
// Acquires/updates the lease to the blob.
// TODO(b/149260496): catch LimitExceededException, thrown when a lease could not be acquired,
// such as when the caller is trying to acquire leases on too much data.
try (OutputStream out = fileStorage.open(leaseUri, WriteStreamOpener.create())) {}
} catch (UnsupportedFileStorageOperation e) {
String msg = TextUtils.isEmpty(e.getMessage()) ? "" : e.getMessage();
LogUtil.v(
"%s: Failed to share file %s, file group %s."
+ " UnsupportedFileStorageOperation was thrown with message \"%s\"",
TAG, dataFile.getFileId(), fileGroup.getGroupName(), msg);
errorCode = 0;
message = "UnsupportedFileStorageOperation was thrown: " + msg;
} catch (MalformedUriException e) {
LogUtil.e(
"%s: Malformed lease uri file %s, file group %s",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"Malformed lease Uri for file %s, group %s",
dataFile.getFileId(), fileGroup.getGroupName());
} catch (LimitExceededException e) {
LogUtil.e(
"%s: Failed to share after download for file %s, file group %s due to"
+ " LimitExceededException",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"System limit exceeded for file %s, group %s",
dataFile.getFileId(), fileGroup.getGroupName());
} catch (IOException e) {
LogUtil.e(
"%s: Failed to acquire lease for file %s, file group" + " %s",
TAG, dataFile.getFileId(), fileGroup.getGroupName());
errorCode = 0;
message =
String.format(
"Error while acquiring lease for file %s, group %s",
dataFile.getFileId(), fileGroup.getGroupName());
}
if (errorCode != -1) {
throw new AndroidSharingException(errorCode, message);
}
}
}