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