• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 package android.app.appsearch;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.appsearch.exceptions.AppSearchException;
22 import android.util.Log;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import java.io.IOException;
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.Objects;
30 
31 /**
32  * Information about the success or failure of an AppSearch call.
33  *
34  * @param <ValueType> The type of result object for successful calls.
35  */
36 public final class AppSearchResult<ValueType> {
37     private static final String TAG = "AppSearchResult";
38 
39     /**
40      * Result codes from {@link AppSearchSession} methods.
41      *
42      * @hide
43      */
44     @IntDef(
45             value = {
46                 RESULT_OK,
47                 RESULT_UNKNOWN_ERROR,
48                 RESULT_INTERNAL_ERROR,
49                 RESULT_INVALID_ARGUMENT,
50                 RESULT_IO_ERROR,
51                 RESULT_OUT_OF_SPACE,
52                 RESULT_NOT_FOUND,
53                 RESULT_INVALID_SCHEMA,
54                 RESULT_SECURITY_ERROR,
55             })
56     @Retention(RetentionPolicy.SOURCE)
57     public @interface ResultCode {}
58 
59     /** The call was successful. */
60     public static final int RESULT_OK = 0;
61 
62     /** An unknown error occurred while processing the call. */
63     public static final int RESULT_UNKNOWN_ERROR = 1;
64 
65     /**
66      * An internal error occurred within AppSearch, which the caller cannot address.
67      *
68      * <p>This error may be considered similar to {@link IllegalStateException}
69      */
70     public static final int RESULT_INTERNAL_ERROR = 2;
71 
72     /**
73      * The caller supplied invalid arguments to the call.
74      *
75      * <p>This error may be considered similar to {@link IllegalArgumentException}.
76      */
77     public static final int RESULT_INVALID_ARGUMENT = 3;
78 
79     /**
80      * An issue occurred reading or writing to storage. The call might succeed if repeated.
81      *
82      * <p>This error may be considered similar to {@link java.io.IOException}.
83      */
84     public static final int RESULT_IO_ERROR = 4;
85 
86     /** Storage is out of space, and no more space could be reclaimed. */
87     public static final int RESULT_OUT_OF_SPACE = 5;
88 
89     /** An entity the caller requested to interact with does not exist in the system. */
90     public static final int RESULT_NOT_FOUND = 6;
91 
92     /** The caller supplied a schema which is invalid or incompatible with the previous schema. */
93     public static final int RESULT_INVALID_SCHEMA = 7;
94 
95     /** The caller requested an operation it does not have privileges for. */
96     public static final int RESULT_SECURITY_ERROR = 8;
97 
98     private final @ResultCode int mResultCode;
99     @Nullable private final ValueType mResultValue;
100     @Nullable private final String mErrorMessage;
101 
AppSearchResult( @esultCode int resultCode, @Nullable ValueType resultValue, @Nullable String errorMessage)102     private AppSearchResult(
103             @ResultCode int resultCode,
104             @Nullable ValueType resultValue,
105             @Nullable String errorMessage) {
106         mResultCode = resultCode;
107         mResultValue = resultValue;
108         mErrorMessage = errorMessage;
109     }
110 
111     /** Returns {@code true} if {@link #getResultCode} equals {@link AppSearchResult#RESULT_OK}. */
isSuccess()112     public boolean isSuccess() {
113         return getResultCode() == RESULT_OK;
114     }
115 
116     /** Returns one of the {@code RESULT} constants defined in {@link AppSearchResult}. */
getResultCode()117     public @ResultCode int getResultCode() {
118         return mResultCode;
119     }
120 
121     /**
122      * Returns the result value associated with this result, if it was successful.
123      *
124      * <p>See the documentation of the particular {@link AppSearchSession} call producing this
125      * {@link AppSearchResult} for what is placed in the result value by that call.
126      *
127      * @throws IllegalStateException if this {@link AppSearchResult} is not successful.
128      */
129     @Nullable
getResultValue()130     public ValueType getResultValue() {
131         if (!isSuccess()) {
132             throw new IllegalStateException("AppSearchResult is a failure: " + this);
133         }
134         return mResultValue;
135     }
136 
137     /**
138      * Returns the error message associated with this result.
139      *
140      * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
141      * message may be {@code null} even if {@link #isSuccess} is {@code false}. See the
142      * documentation of the particular {@link AppSearchSession} call producing this {@link
143      * AppSearchResult} for what is returned by {@link #getErrorMessage}.
144      */
145     @Nullable
getErrorMessage()146     public String getErrorMessage() {
147         return mErrorMessage;
148     }
149 
150     @Override
equals(@ullable Object other)151     public boolean equals(@Nullable Object other) {
152         if (this == other) {
153             return true;
154         }
155         if (!(other instanceof AppSearchResult)) {
156             return false;
157         }
158         AppSearchResult<?> otherResult = (AppSearchResult<?>) other;
159         return mResultCode == otherResult.mResultCode
160                 && Objects.equals(mResultValue, otherResult.mResultValue)
161                 && Objects.equals(mErrorMessage, otherResult.mErrorMessage);
162     }
163 
164     @Override
hashCode()165     public int hashCode() {
166         return Objects.hash(mResultCode, mResultValue, mErrorMessage);
167     }
168 
169     @Override
170     @NonNull
toString()171     public String toString() {
172         if (isSuccess()) {
173             return "[SUCCESS]: " + mResultValue;
174         }
175         return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
176     }
177 
178     /**
179      * Creates a new successful {@link AppSearchResult}.
180      *
181      * @param value An optional value to associate with the successful result of the operation being
182      *     performed.
183      */
184     @NonNull
newSuccessfulResult( @ullable ValueType value)185     public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult(
186             @Nullable ValueType value) {
187         return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null);
188     }
189 
190     /**
191      * Creates a new failed {@link AppSearchResult}.
192      *
193      * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
194      * @param errorMessage An optional string describing the reason or nature of the failure.
195      */
196     @NonNull
newFailedResult( @esultCode int resultCode, @Nullable String errorMessage)197     public static <ValueType> AppSearchResult<ValueType> newFailedResult(
198             @ResultCode int resultCode, @Nullable String errorMessage) {
199         return new AppSearchResult<>(resultCode, /*resultValue=*/ null, errorMessage);
200     }
201 
202     /**
203      * Creates a new failed {@link AppSearchResult} by a AppSearchResult in another type.
204      *
205      * @hide
206      */
207     @NonNull
newFailedResult( @onNull AppSearchResult<?> otherFailedResult)208     public static <ValueType> AppSearchResult<ValueType> newFailedResult(
209             @NonNull AppSearchResult<?> otherFailedResult) {
210         Preconditions.checkState(
211                 !otherFailedResult.isSuccess(),
212                 "Cannot convert a success result to a failed result");
213         return AppSearchResult.newFailedResult(
214                 otherFailedResult.getResultCode(), otherFailedResult.getErrorMessage());
215     }
216 
217     /** @hide */
218     @NonNull
throwableToFailedResult( @onNull Throwable t)219     public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
220             @NonNull Throwable t) {
221         // Log for traceability. NOT_FOUND is logged at VERBOSE because this error can occur during
222         // the regular operation of the system (b/183550974). Everything else is logged at DEBUG.
223         if (t instanceof AppSearchException
224                 && ((AppSearchException) t).getResultCode() == RESULT_NOT_FOUND) {
225             Log.v(TAG, "Converting throwable to failed result: " + t);
226         } else {
227             Log.d(TAG, "Converting throwable to failed result.", t);
228         }
229 
230         if (t instanceof AppSearchException) {
231             return ((AppSearchException) t).toAppSearchResult();
232         }
233 
234         String exceptionClass = t.getClass().getSimpleName();
235         @AppSearchResult.ResultCode int resultCode;
236         if (t instanceof IllegalStateException || t instanceof NullPointerException) {
237             resultCode = AppSearchResult.RESULT_INTERNAL_ERROR;
238         } else if (t instanceof IllegalArgumentException) {
239             resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT;
240         } else if (t instanceof IOException) {
241             resultCode = AppSearchResult.RESULT_IO_ERROR;
242         } else if (t instanceof SecurityException) {
243             resultCode = AppSearchResult.RESULT_SECURITY_ERROR;
244         } else {
245             resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
246         }
247         return AppSearchResult.newFailedResult(resultCode, exceptionClass + ": " + t.getMessage());
248     }
249 }
250