Skip to content

Added FirebaseMlException and Logging round 1. #2259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion firebase-ml-modeldownloader/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.google.firebase.ml.modeldownloader {

public class CustomModel {
method public long getDownloadId();
method @Nullable public java.io.File getFile() throws java.lang.Exception;
method @Nullable public java.io.File getFile() throws com.google.firebase.ml.modeldownloader.FirebaseMlException;
method @NonNull public String getModelHash();
method @NonNull public String getName();
method public long getSize();
Expand All @@ -29,12 +29,36 @@ package com.google.firebase.ml.modeldownloader {
enum_constant public static final com.google.firebase.ml.modeldownloader.DownloadType LOCAL_MODEL_UPDATE_IN_BACKGROUND;
}

public class FirebaseMlException extends com.google.firebase.FirebaseException {
method @com.google.firebase.ml.modeldownloader.FirebaseMlException.Code public int getCode();
field public static final int ABORTED = 10; // 0xa
field public static final int ALREADY_EXISTS = 6; // 0x6
field public static final int CANCELLED = 1; // 0x1
field public static final int DEADLINE_EXCEEDED = 4; // 0x4
field public static final int FAILED_PRECONDITION = 9; // 0x9
field public static final int INTERNAL = 13; // 0xd
field public static final int INVALID_ARGUMENT = 3; // 0x3
field public static final int NOT_ENOUGH_SPACE = 101; // 0x65
field public static final int NOT_FOUND = 5; // 0x5
field public static final int OUT_OF_RANGE = 11; // 0xb
field public static final int PERMISSION_DENIED = 7; // 0x7
field public static final int RESOURCE_EXHAUSTED = 8; // 0x8
field public static final int UNAUTHENTICATED = 16; // 0x10
field public static final int UNAVAILABLE = 14; // 0xe
field public static final int UNIMPLEMENTED = 12; // 0xc
field public static final int UNKNOWN = 2; // 0x2
}

@IntDef({com.google.firebase.ml.modeldownloader.FirebaseMlException.CANCELLED, com.google.firebase.ml.modeldownloader.FirebaseMlException.UNKNOWN, com.google.firebase.ml.modeldownloader.FirebaseMlException.INVALID_ARGUMENT, com.google.firebase.ml.modeldownloader.FirebaseMlException.DEADLINE_EXCEEDED, com.google.firebase.ml.modeldownloader.FirebaseMlException.NOT_FOUND, com.google.firebase.ml.modeldownloader.FirebaseMlException.ALREADY_EXISTS, com.google.firebase.ml.modeldownloader.FirebaseMlException.PERMISSION_DENIED, com.google.firebase.ml.modeldownloader.FirebaseMlException.RESOURCE_EXHAUSTED, com.google.firebase.ml.modeldownloader.FirebaseMlException.FAILED_PRECONDITION, com.google.firebase.ml.modeldownloader.FirebaseMlException.ABORTED, com.google.firebase.ml.modeldownloader.FirebaseMlException.OUT_OF_RANGE, com.google.firebase.ml.modeldownloader.FirebaseMlException.UNIMPLEMENTED, com.google.firebase.ml.modeldownloader.FirebaseMlException.INTERNAL, com.google.firebase.ml.modeldownloader.FirebaseMlException.UNAVAILABLE, com.google.firebase.ml.modeldownloader.FirebaseMlException.UNAUTHENTICATED, com.google.firebase.ml.modeldownloader.FirebaseMlException.NOT_ENOUGH_SPACE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface FirebaseMlException.Code {
}

public class FirebaseModelDownloader {
method @NonNull public com.google.android.gms.tasks.Task<java.lang.Void> deleteDownloadedModel(@NonNull String);
method @NonNull public static com.google.firebase.ml.modeldownloader.FirebaseModelDownloader getInstance();
method @NonNull public static com.google.firebase.ml.modeldownloader.FirebaseModelDownloader getInstance(@NonNull com.google.firebase.FirebaseApp);
method @NonNull public com.google.android.gms.tasks.Task<com.google.firebase.ml.modeldownloader.CustomModel> getModel(@NonNull String, @NonNull com.google.firebase.ml.modeldownloader.DownloadType, @Nullable com.google.firebase.ml.modeldownloader.CustomModelDownloadConditions) throws java.lang.Exception;
method @NonNull public com.google.android.gms.tasks.Task<java.util.Set<com.google.firebase.ml.modeldownloader.CustomModel>> listDownloadedModels();
method public void setStatsCollectionEnabled(boolean);
}

}
Expand Down
31 changes: 28 additions & 3 deletions firebase-ml-modeldownloader/firebase-ml-modeldownloader.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,29 @@

plugins {
id 'firebase-library'
id 'com.google.protobuf'
}

firebaseLibrary {
testLab.enabled = false
publishJavadoc = false
}

protobuf {
// Configure the protoc executable
protoc {
// Download from repositories
artifact = "com.google.protobuf:protoc:$protocVersion"
}
generateProtoTasks {
all().each { task ->
task.builtins {
java { }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add option 'lite' (see firebase-firestore.gradle )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I add that then - JsonFormat.parser().merge fails (based on testLogRequest_jsontToProto) which doesn't use lite option. Can you suggest an alternative?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note - the proto is only for testing and shouldn't affect the sdk sizing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right. JsonFormat parser actually requires the full runtime to work. Since it's used for tests, I think you can do the same crashlytics is doing https://github.com/firebase/firebase-android-sdk/blob/master/firebase-crashlytics/firebase-crashlytics.gradle#L88

just use testImplementation instead of androidTestImplementation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed all the androidTestImplementation - these were not used. The encoding was already there for the regular implementation level (and validated it is needed at that level)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK you can just get rid of the whole generateProtoTasks sections.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appears required - gives error when removed:
Execution failed for task ':firebase-ml-modeldownloader:generateDebugUnitTestProto'.
protoc: stdout: . stderr: Missing output directives.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thanks for following through!

}
}
}
}

android {
compileSdkVersion project.targetSdkVersion

Expand All @@ -43,25 +59,34 @@ android {
}

dependencies {
implementation project(':encoders:firebase-encoders')
implementation project(':encoders:firebase-encoders-json')
implementation project(':firebase-common')
implementation project(':firebase-components')
implementation project(':firebase-datatransport')
implementation project(':firebase-installations-interop')
implementation project(':transport:transport-api')
implementation project(':transport:transport-runtime')
runtimeOnly project(':firebase-installations')

implementation 'androidx.annotation:annotation:1.1.0'
implementation 'com.google.android.gms:play-services-tasks:17.2.0'
implementation 'com.google.auto.service:auto-service-annotations:1.0-rc6'
implementation 'javax.inject:javax.inject:1'

compileOnly "com.google.auto.value:auto-value-annotations:1.6.6"
annotationProcessor "com.google.auto.value:auto-value:1.6.5"
annotationProcessor project(":encoders:firebase-encoders-processor")

testImplementation 'androidx.test:core:1.3.0'
testImplementation 'com.github.tomakehurst:wiremock-standalone:2.26.3'
testImplementation "com.google.truth:truth:$googleTruthVersion"
testImplementation 'junit:junit:4.13.1'
testImplementation "org.robolectric:robolectric:$robolectricVersion"
testImplementation 'com.google.truth.extensions:truth-proto-extension:1.0'
testImplementation 'com.google.protobuf:protobuf-java-util:3.11.0'
testImplementation 'junit:junit:4.13-beta-2'

//Android compatible version of Apache httpclient.
testImplementation 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
testImplementation 'org.mockito:mockito-core:3.3.3'
testImplementation "org.robolectric:robolectric:$robolectricVersion"
testImplementation 'com.google.truth.extensions:truth-proto-extension:1.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public String getName() {
* progress, returns null, if file update is in progress returns last fully uploaded model.
*/
@Nullable
public File getFile() throws Exception {
public File getFile() throws FirebaseMlException {
return getFile(ModelFileDownloadService.getInstance());
}

Expand All @@ -142,7 +142,7 @@ public File getFile() throws Exception {
*/
@Nullable
@VisibleForTesting
File getFile(ModelFileDownloadService fileDownloadService) throws Exception {
File getFile(ModelFileDownloadService fileDownloadService) throws FirebaseMlException {
// check for completed download
File newDownloadFile = fileDownloadService.loadNewlyDownloadedModelFile(this);
if (newDownloadFile != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2020 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.firebase.ml.modeldownloader;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.gms.common.internal.Preconditions;
import com.google.firebase.FirebaseException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* Represents an Exception resulting from an operation on a {@link FirebaseModelDownloader}. Error
* mappings should remain consistent with the original firebase_ml_sdk whenever possible.
*/
public class FirebaseMlException extends FirebaseException {
/** The operation was cancelled (typically by the caller). */
public static final int CANCELLED = 1;

/** Unknown error or an error from a different error domain. */
public static final int UNKNOWN = 2;

/**
* Client specified an invalid argument. Note that this differs from FAILED_PRECONDITION.
* INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the system
* (e.g., an invalid field name).
*/
public static final int INVALID_ARGUMENT = 3;

/**
* Deadline expired before operation could complete. For operations that change the state of the
* system, this error may be returned even if the operation has completed successfully. For
* example, a successful response from a server could have been delayed long enough for the
* deadline to expire.
*/
public static final int DEADLINE_EXCEEDED = 4;

/** Some requested resource was not found. */
public static final int NOT_FOUND = 5;

/** Some resource that we attempted to create already exists. */
public static final int ALREADY_EXISTS = 6;

/** The caller does not have permission to execute the specified operation. */
public static final int PERMISSION_DENIED = 7;

/**
* Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system
* is out of space.
*/
public static final int RESOURCE_EXHAUSTED = 8;

/**
* Operation was rejected because the system is not in a state required for the operation's
* execution.
*/
public static final int FAILED_PRECONDITION = 9;

/**
* The operation was aborted, typically due to a concurrency issue like transaction aborts, etc.
*/
public static final int ABORTED = 10;

/** Operation was attempted past the valid range. */
public static final int OUT_OF_RANGE = 11;

/** Operation is not implemented or not supported/enabled. */
public static final int UNIMPLEMENTED = 12;

/**
* Internal errors. Means some invariants expected by underlying system has been broken. If you
* see one of these errors, something is very broken.
*/
// annz used
public static final int INTERNAL = 13;

/**
* The service is currently unavailable. This is a most likely a transient condition and may be
* corrected by retrying with a backoff.
*
* <p>In ML Kit, this error is mostly about the models being not available yet.
*/
public static final int UNAVAILABLE = 14;

/** The request does not have valid authentication credentials for the operation. */
public static final int UNAUTHENTICATED = 16;

// ===============================================================================================
// Error codes: 100 to 149 reserved for errors during model downloading/loading.
/** There is not enough space left on the device. */
// annz used
public static final int NOT_ENOUGH_SPACE = 101;

/**
* The set of Firebase ML status codes. The codes are based on <a
* href="https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto">Canonical
* error codes for Google APIs</a>
*/
@IntDef({
CANCELLED,
UNKNOWN,
INVALID_ARGUMENT,
DEADLINE_EXCEEDED,
NOT_FOUND,
ALREADY_EXISTS,
PERMISSION_DENIED,
RESOURCE_EXHAUSTED,
FAILED_PRECONDITION,
ABORTED,
OUT_OF_RANGE,
UNIMPLEMENTED,
INTERNAL,
UNAVAILABLE,
UNAUTHENTICATED,
NOT_ENOUGH_SPACE
})
@Retention(RetentionPolicy.CLASS)
public @interface Code {}

@Code private final int code;

/** @hide */
public FirebaseMlException(@NonNull String detailMessage, @Code int code) {
super(Preconditions.checkNotEmpty(detailMessage, "Provided message must not be empty."));
this.code = code;
}

/** @hide */
public FirebaseMlException(
@NonNull String detailMessage, @Code int code, @Nullable Throwable cause) {
super(Preconditions.checkNotEmpty(detailMessage, "Provided message must not be empty."), cause);
this.code = code;
}

/** Gets the error code for the Firebase ML operation that failed. */
@Code
public int getCode() {
return code;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import com.google.android.datatransport.TransportFactory;
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
Expand Down Expand Up @@ -45,9 +46,11 @@ public class FirebaseModelDownloader {

@RequiresApi(api = VERSION_CODES.KITKAT)
FirebaseModelDownloader(
FirebaseApp firebaseApp, FirebaseInstallationsApi firebaseInstallationsApi) {
FirebaseApp firebaseApp,
FirebaseInstallationsApi firebaseInstallationsApi,
TransportFactory transportFactory) {
this.firebaseOptions = firebaseApp.getOptions();
this.fileDownloadService = new ModelFileDownloadService(firebaseApp);
this.fileDownloadService = new ModelFileDownloadService(firebaseApp, transportFactory);
this.sharedPreferencesUtil = new SharedPreferencesUtil(firebaseApp);
this.modelDownloadService =
new CustomModelDownloadService(firebaseOptions, firebaseInstallationsApi);
Expand Down Expand Up @@ -318,6 +321,15 @@ public Task<Void> deleteDownloadedModel(@NonNull String modelName) {
return taskCompletionSource.getTask();
}

/**
* Update the settings which allow logging to firelog.
*
* @param enabled - is statistics logging enabled
*/
public void setStatsCollectionEnabled(boolean enabled) {
sharedPreferencesUtil.setCustomModelStatsCollectionEnabled(enabled);
}

/** Returns the nick name of the {@link FirebaseApp} of this {@link FirebaseModelDownloader} */
@VisibleForTesting
String getApplicationId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.os.Build.VERSION_CODES;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.google.android.datatransport.TransportFactory;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.components.Component;
Expand Down Expand Up @@ -46,11 +47,14 @@ public List<Component<?>> getComponents() {
return Arrays.asList(
Component.builder(FirebaseModelDownloader.class)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.required(TransportFactory.class))
.add(Dependency.required(FirebaseInstallationsApi.class))
.factory(
c ->
new FirebaseModelDownloader(
c.get(FirebaseApp.class), c.get(FirebaseInstallationsApi.class)))
c.get(FirebaseApp.class),
c.get(FirebaseInstallationsApi.class),
c.get(TransportFactory.class)))
.build(),
Component.builder(SharedPreferencesUtil.class)
.add(Dependency.required(FirebaseApp.class))
Expand All @@ -62,7 +66,11 @@ public List<Component<?>> getComponents() {
.build(),
Component.builder(ModelFileDownloadService.class)
.add(Dependency.required(FirebaseApp.class))
.factory(c -> new ModelFileDownloadService(c.get(FirebaseApp.class)))
.add(Dependency.required(TransportFactory.class))
.factory(
c ->
new ModelFileDownloadService(
c.get(FirebaseApp.class), c.get(TransportFactory.class)))
.build(),
Component.builder(CustomModelDownloadService.class)
.add(Dependency.required(FirebaseOptions.class))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,12 @@ private CustomModel readCustomModelResponse(
expireTime = parseTokenExpirationTimestamp(reader.nextString());
} else if (name.equals("sizeBytes")) {
fileSize = reader.nextLong();
} else if (name.equals("modelFormat")) {
String modelFormat = reader.nextString();
if (modelFormat.equals("MODEL_FORMAT_UNSPECIFIED")) {
// log error but continue... this shouldn't happen
Log.w(TAG, "Ignoring unexpected model type: " + modelFormat);
}
} else {
reader.skipValue();
}
Expand Down
Loading