diff options
| author | Winson Chiu <chiuwinson@google.com> | 2020-04-01 05:44:35 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-04-01 05:44:35 +0000 |
| commit | bde104f8be331b97ba8bdb9f0a809245e785ef3f (patch) | |
| tree | d2d26fa256b278c9875888882babd6bae12ebd4a /core/java/android | |
| parent | 456365644b157791e41aaa680ffa31634c8c03e1 (diff) | |
| parent | db4355fed9a80951e65a80a3682e206247759b1a (diff) | |
Merge changes Id7d1ff26,Id53a2e65 into rvc-dev
* changes:
Add am compat enable/disable/reset test for not installed app
Gate stricter manifest enforcement on targetSdk R
Diffstat (limited to 'core/java/android')
7 files changed, 286 insertions, 100 deletions
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java index e90ccdffa8a8..12328cf32fb3 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java @@ -65,7 +65,9 @@ import android.content.pm.parsing.component.ParsedProviderUtils; import android.content.pm.parsing.component.ParsedService; import android.content.pm.parsing.component.ParsedServiceUtils; import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseInput.DeferredError; import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; @@ -126,6 +128,51 @@ public class ParsingPackageUtils { public static final String TAG = ParsingUtils.TAG; + /** + * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off + * request, without caching the input object and without querying the internal system state + * for feature support. + */ + @NonNull + public static ParseResult<ParsingPackage> parseDefaultOneTime(File file, int flags, + @NonNull ParseInput.Callback inputCallback, @NonNull Callback callback) { + if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) { + // Caller expressed no opinion about what encryption + // aware/unaware components they want to see, so match both + flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + } + + ParseInput input = new ParseTypeImpl(inputCallback).reset(); + ParseResult<ParsingPackage> result; + + + ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, callback); + try { + result = parser.parsePackage(input, file, flags); + if (result.isError()) { + return result; + } + } catch (PackageParser.PackageParserException e) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Error parsing package", e); + } + + try { + ParsingPackage pkg = result.getResult(); + if ((flags & PackageManager.GET_SIGNATURES) != 0 + || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) { + ParsingPackageUtils.collectCertificates(pkg, false /* skipVerify */); + } + + return input.success(pkg); + } catch (PackageParser.PackageParserException e) { + return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Error collecting package certificates", e); + } + } + private boolean mOnlyCoreApps; private String[] mSeparateProcesses; private DisplayMetrics mDisplayMetrics; @@ -456,10 +503,11 @@ public class ParsingPackageUtils { } if (!foundApp) { - return input.error( - PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY, - "<manifest> does not contain an <application>" - ); + ParseResult<?> deferResult = input.deferError( + "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG); + if (deferResult.isError()) { + return input.error(deferResult); + } } return input.success(pkg); @@ -663,10 +711,12 @@ public class ParsingPackageUtils { } if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) { - return input.error( - PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY, - "<manifest> does not contain an <application> or <instrumentation>" - ); + ParseResult<?> deferResult = input.deferError( + "<manifest> does not contain an <application> or <instrumentation>", + DeferredError.MISSING_APP_TAG); + if (deferResult.isError()) { + return input.error(deferResult); + } } if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) { @@ -758,11 +808,9 @@ public class ParsingPackageUtils { return input.success(pkg); } - ParseResult nameResult = validateName(input, str, true, true); - if (nameResult.isError()) { - if ("android".equals(pkg.getPackageName())) { - nameResult.ignoreError(); - } else { + if (!"android".equals(pkg.getPackageName())) { + ParseResult<?> nameResult = validateName(input, str, true, true); + if (nameResult.isError()) { return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, "<manifest> specifies bad sharedUserId name \"" + str + "\": " + nameResult.getErrorMessage()); @@ -1166,6 +1214,20 @@ public class ParsingPackageUtils { targetCode = minCode; } + ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion( + targetVers, targetCode, PackageParser.SDK_CODENAMES, input); + if (targetSdkVersionResult.isError()) { + return input.error(targetSdkVersionResult); + } + + int targetSdkVersion = targetSdkVersionResult.getResult(); + + ParseResult<?> deferResult = + input.enableDeferredError(pkg.getPackageName(), targetSdkVersion); + if (deferResult.isError()) { + return input.error(deferResult); + } + ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode, PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input); if (minSdkVersionResult.isError()) { @@ -1174,14 +1236,6 @@ public class ParsingPackageUtils { int minSdkVersion = minSdkVersionResult.getResult(); - ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion( - targetVers, targetCode, PackageParser.SDK_CODENAMES, input); - if (targetSdkVersionResult.isError()) { - return input.error(targetSdkVersionResult); - } - - int targetSdkVersion = minSdkVersionResult.getResult(); - pkg.setMinSdkVersion(minSdkVersion) .setTargetSdkVersion(targetSdkVersion); @@ -1763,9 +1817,15 @@ public class ParsingPackageUtils { // Add a hidden app detail activity to normal apps which forwards user to App Details // page. ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg); - // Backwards-compat, assume success + if (a.isError()) { + // Error should be impossible here, as the only failure case as of SDK R is a + // string validation error on a constant ":app_details" string passed in by the + // parsing code itself. For this reason, this is just a hard failure instead of + // deferred. + return input.error(a); + } + pkg.addActivity(a.getResult()); - a.ignoreError(); } if (hasActivityOrder) { @@ -2122,18 +2182,14 @@ public class ParsingPackageUtils { private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input, ParsingPackage pkg) { String packageName = pkg.getPackageName(); - ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName( + ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName( packageName, packageName, ":app_details", input); - - String taskAffinity; - if (taskAffinityResult.isSuccess()) { - taskAffinity = taskAffinityResult.getResult(); - } else { - // Backwards-compat, do not fail - taskAffinity = null; - taskAffinityResult.ignoreError(); + if (result.isError()) { + return input.error(result); } + String taskAffinity = result.getResult(); + // Build custom App Details activity info instead of parsing it from xml return input.success(ParsedActivity.makeAppDetailsActivity(packageName, pkg.getProcessName(), pkg.getUiOptions(), taskAffinity, @@ -2688,7 +2744,8 @@ public class ParsingPackageUtils { public interface Callback { boolean hasFeature(String feature); - ParsingPackage startParsingPackage(String packageName, String baseCodePath, String codePath, + ParsingPackage startParsingPackage(@NonNull String packageName, + @NonNull String baseCodePath, @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp); } } diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java index 6e5c51a22025..f64560a14832 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java @@ -179,13 +179,12 @@ public class ParsedActivityUtils { ParseResult<String> affinityNameResult = ComponentParseUtils.buildTaskAffinityName( packageName, pkg.getTaskAffinity(), taskAffinity, input); - if (affinityNameResult.isSuccess()) { - activity.taskAffinity = affinityNameResult.getResult(); - } else { - // Backwards-compat, ignore error - affinityNameResult.ignoreError(); + if (affinityNameResult.isError()) { + return input.error(affinityNameResult); } + activity.taskAffinity = affinityNameResult.getResult(); + boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false); if (visibleToEphemeral) { activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java index a7b950b194d2..390f76968e7c 100644 --- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java @@ -29,7 +29,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.PatternMatcher; -import android.text.TextUtils; import android.util.Slog; import android.util.TypedValue; @@ -97,8 +96,13 @@ public class ParsedIntentInfoUtils { case "action": { String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES, "name"); - if (TextUtils.isEmpty(value)) { + if (value == null) { result = input.error("No value supplied for <android:name>"); + } else if (value.isEmpty()) { + intentInfo.addAction(value); + // Prior to R, this was not a failure + result = input.deferError("No value supplied for <android:name>", + ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY); } else { intentInfo.addAction(value); result = input.success(null); @@ -108,8 +112,13 @@ public class ParsedIntentInfoUtils { case "category": { String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES, "name"); - if (TextUtils.isEmpty(value)) { + if (value == null) { result = input.error("No value supplied for <android:name>"); + } else if (value.isEmpty()) { + intentInfo.addCategory(value); + // Prior to R, this was not a failure + result = input.deferError("No value supplied for <android:name>", + ParseInput.DeferredError.EMPTY_INTENT_ACTION_CATEGORY); } else { intentInfo.addCategory(value); result = input.success(null); diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java index 6188f8933ab2..f4c9914cb69f 100644 --- a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java @@ -20,6 +20,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.IntentFilter; import android.content.pm.parsing.ParsingPackage; +import android.content.pm.parsing.ParsingPackageUtils; +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseResult; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; @@ -28,9 +31,6 @@ import android.os.Build; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import android.content.pm.parsing.ParsingPackageUtils; -import android.content.pm.parsing.result.ParseInput; -import android.content.pm.parsing.result.ParseResult; import org.xmlpull.v1.XmlPullParserException; @@ -83,12 +83,11 @@ class ParsedMainComponentUtils { ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName( pkg.getPackageName(), pkg.getProcessName(), processName, flags, separateProcesses, input); - if (processNameResult.isSuccess()) { - component.setProcessName(processNameResult.getResult()); - } else { - // Backwards-compat, ignore error - processNameResult.ignoreError(); + if (processNameResult.isError()) { + return input.error(processNameResult); } + + component.setProcessName(processNameResult.getResult()); } if (splitNameAttr != null) { diff --git a/core/java/android/content/pm/parsing/result/ParseInput.java b/core/java/android/content/pm/parsing/result/ParseInput.java index c46850609d9e..538510049e04 100644 --- a/core/java/android/content/pm/parsing/result/ParseInput.java +++ b/core/java/android/content/pm/parsing/result/ParseInput.java @@ -16,10 +16,12 @@ package android.content.pm.parsing.result; -import android.annotation.Hide; import android.annotation.NonNull; import android.annotation.Nullable; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.pm.PackageManager; +import android.os.Build; /** * Used as a method parameter which is then transformed into a {@link ParseResult}. This is @@ -30,8 +32,52 @@ import android.content.pm.PackageManager; */ public interface ParseInput { + /** + * Errors encountered during parsing may rely on the targetSDK version of the application to + * determine whether or not to fail. These are passed into {@link #deferError(String, long)} + * when encountered, and the implementation will handle how to defer the errors until the + * targetSdkVersion is known and sent to {@link #enableDeferredError(String, int)}. + * + * All of these must be marked {@link ChangeId}, as that is the mechanism used to check if the + * error must be propagated. This framework also allows developers to pre-disable specific + * checks if they wish to target a newer SDK version in a development environment without + * having to migrate their entire app to validate on a newer platform. + */ + final class DeferredError { + /** + * Missing an "application" or "instrumentation" tag. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + public static final long MISSING_APP_TAG = 150776642; + + /** + * An intent filter's actor or category is an empty string. A bug in the platform before R + * allowed this to pass through without an error. This does not include cases when the + * attribute is null/missing, as that has always been a failure. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + public static final long EMPTY_INTENT_ACTION_CATEGORY = 151163173; + } + <ResultType> ParseResult<ResultType> success(ResultType result); + /** + * Used for errors gated by {@link DeferredError}. Will return an error result if the + * targetSdkVersion is already known and this must be returned as a real error. The result + * contains null and should not be unwrapped. + * + * @see #error(String) + */ + ParseResult<?> deferError(@NonNull String parseError, long deferredError); + + /** + * Called after targetSdkVersion is known. Returns an error result if a previously deferred + * error was registered. The result contains null and should not be unwrapped. + */ + ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion); + /** @see #error(int, String, Exception) */ <ResultType> ParseResult<ResultType> error(int parseError); @@ -52,9 +98,6 @@ public interface ParseInput { * The calling site of that method is then expected to check the result for error, and * continue to bubble up if it is an error. * - * Or, if the code explicitly handles an error, - * {@link ParseResult#ignoreError()} should be called. - * * If the result {@link ParseResult#isSuccess()}, then it can be used as-is, as * overlapping/consecutive successes are allowed. */ @@ -66,5 +109,17 @@ public interface ParseInput { * but cast the type of the {@link ParseResult} for type safety, since the parameter * and the receiver should be the same object. */ - <ResultType> ParseResult<ResultType> error(ParseResult result); + <ResultType> ParseResult<ResultType> error(ParseResult<?> result); + + /** + * Implemented instead of a direct reference to + * {@link com.android.internal.compat.IPlatformCompat}, allowing caching and testing logic to + * be separated out. + */ + interface Callback { + /** + * @return true if the changeId should be enabled + */ + boolean isChangeEnabled(long changeId, @NonNull String packageName, int targetSdkVersion); + } } diff --git a/core/java/android/content/pm/parsing/result/ParseResult.java b/core/java/android/content/pm/parsing/result/ParseResult.java index 338048c3c1ec..518395d7d392 100644 --- a/core/java/android/content/pm/parsing/result/ParseResult.java +++ b/core/java/android/content/pm/parsing/result/ParseResult.java @@ -17,32 +17,19 @@ package android.content.pm.parsing.result; import android.annotation.Nullable; -import android.content.pm.PackageParser; /** * The output side of {@link ParseInput}, which must result from a method call on * {@link ParseInput}. * * When using this class, keep in mind that all {@link ParseInput}s and {@link ParseResult}s - * are the exact same object, scoped to a per {@link PackageParser} instance, per thread basis, - * thrown around and casted everywhere for type safety. + * are the exact same object, scoped per thread, thrown around and casted for type safety. * * @hide */ public interface ParseResult<ResultType> { /** - * Un-marks this result as an error, also allowing it to be re-used as {@link ParseInput}. - * - * This should only be used in cases where it's absolutely certain that error handling is - * irrelevant. Such as for backwards compatibility where it previously didn't fail and that - * behavior has to be maintained. - * - * Mostly an alias for readability. - */ - void ignoreError(); - - /** * Returns true if the result is not an error and thus contains a valid object. * * For backwards-compat reasons, it's possible to have a successful result with a null diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java index 9b22f09b2978..b26bf71a61c5 100644 --- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java +++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java @@ -20,54 +20,133 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; import android.content.pm.parsing.ParsingUtils; +import android.util.ArrayMap; import android.util.Log; +import android.util.Slog; -import java.util.Arrays; +import com.android.internal.util.CollectionUtils; /** @hide */ public class ParseTypeImpl implements ParseInput, ParseResult<Object> { private static final String TAG = ParsingUtils.TAG; - private static final boolean DEBUG_FILL_STACK_TRACE = false; + public static final boolean DEBUG_FILL_STACK_TRACE = false; - private static final boolean DEBUG_LOG_ON_ERROR = false; + public static final boolean DEBUG_LOG_ON_ERROR = false; - private Object result; + public static final boolean DEBUG_THROW_ALL_ERRORS = false; - private int errorCode = PackageManager.INSTALL_SUCCEEDED; + @NonNull + private Callback mCallback; + + private Object mResult; + + private int mErrorCode = PackageManager.INSTALL_SUCCEEDED; @Nullable - private String errorMessage; + private String mErrorMessage; @Nullable - private Exception exception; + private Exception mException; - public ParseInput reset() { - this.result = null; - this.errorCode = PackageManager.INSTALL_SUCCEEDED; - this.errorMessage = null; - this.exception = null; - return this; + /** + * Errors encountered before targetSdkVersion is known. + * The size upper bound is the number of longs in {@link DeferredError} + */ + @Nullable + private ArrayMap<Long, String> mDeferredErrors = null; + + private String mPackageName; + private Integer mTargetSdkVersion; + + /** + * @param callback if nullable, fallback to manual targetSdk > Q check + */ + public ParseTypeImpl(@NonNull Callback callback) { + mCallback = callback; } - @Override - public void ignoreError() { - reset(); + public ParseInput reset() { + mResult = null; + mErrorCode = PackageManager.INSTALL_SUCCEEDED; + mErrorMessage = null; + mException = null; + if (mDeferredErrors != null) { + // If the memory was already allocated, don't bother freeing and re-allocating, + // as this could occur hundreds of times depending on what the caller is doing and + // how many APKs they're going through. + mDeferredErrors.erase(); + } + return this; } @Override public <ResultType> ParseResult<ResultType> success(ResultType result) { - if (errorCode != PackageManager.INSTALL_SUCCEEDED || errorMessage != null) { - throw new IllegalStateException("Cannot set to success after set to error, was " - + errorMessage, exception); + if (mErrorCode != PackageManager.INSTALL_SUCCEEDED) { + Slog.wtf(ParsingUtils.TAG, "Cannot set to success after set to error, was " + + mErrorMessage, mException); } - this.result = result; + mResult = result; //noinspection unchecked return (ParseResult<ResultType>) this; } @Override + public ParseResult<?> deferError(@NonNull String parseError, long deferredError) { + if (DEBUG_THROW_ALL_ERRORS) { + return error(parseError); + } + if (mTargetSdkVersion != null) { + if (mDeferredErrors != null && mDeferredErrors.containsKey(deferredError)) { + // If the map already contains the key, that means it's already been checked and + // found to be disabled. Otherwise it would've failed when mTargetSdkVersion was + // set to non-null. + return success(null); + } + + if (mCallback.isChangeEnabled(deferredError, mPackageName, mTargetSdkVersion)) { + return error(parseError); + } else { + if (mDeferredErrors == null) { + mDeferredErrors = new ArrayMap<>(); + } + mDeferredErrors.put(deferredError, null); + return success(null); + } + } + + if (mDeferredErrors == null) { + mDeferredErrors = new ArrayMap<>(); + } + + // Only save the first occurrence of any particular error + mDeferredErrors.putIfAbsent(deferredError, parseError); + return success(null); + } + + @Override + public ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion) { + mPackageName = packageName; + mTargetSdkVersion = targetSdkVersion; + + int size = CollectionUtils.size(mDeferredErrors); + for (int index = size - 1; index >= 0; index--) { + long changeId = mDeferredErrors.keyAt(index); + String errorMessage = mDeferredErrors.valueAt(index); + if (mCallback.isChangeEnabled(changeId, mPackageName, mTargetSdkVersion)) { + return error(errorMessage); + } else { + // No point holding onto the string, but need to maintain the key to signal + // that the error was checked with isChangeEnabled and found to be disabled. + mDeferredErrors.setValueAt(index, null); + } + } + + return success(null); + } + + @Override public <ResultType> ParseResult<ResultType> error(int parseError) { return error(parseError, null); } @@ -84,25 +163,26 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> { } @Override - public <ResultType> ParseResult<ResultType> error(ParseResult intentResult) { - return error(intentResult.getErrorCode(), intentResult.getErrorMessage()); + public <ResultType> ParseResult<ResultType> error(ParseResult<?> intentResult) { + return error(intentResult.getErrorCode(), intentResult.getErrorMessage(), + intentResult.getException()); } @Override public <ResultType> ParseResult<ResultType> error(int errorCode, @Nullable String errorMessage, Exception exception) { - this.errorCode = errorCode; - this.errorMessage = errorMessage; - this.exception = exception; + mErrorCode = errorCode; + mErrorMessage = errorMessage; + mException = exception; if (DEBUG_FILL_STACK_TRACE) { if (exception == null) { - this.exception = new Exception(); + mException = new Exception(); } } if (DEBUG_LOG_ON_ERROR) { - Exception exceptionToLog = this.exception != null ? this.exception : new Exception(); + Exception exceptionToLog = mException != null ? mException : new Exception(); Log.w(TAG, "ParseInput set to error " + errorCode + ", " + errorMessage, exceptionToLog); } @@ -113,12 +193,12 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> { @Override public Object getResult() { - return this.result; + return mResult; } @Override public boolean isSuccess() { - return errorCode == PackageManager.INSTALL_SUCCEEDED; + return mErrorCode == PackageManager.INSTALL_SUCCEEDED; } @Override @@ -128,18 +208,18 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> { @Override public int getErrorCode() { - return errorCode; + return mErrorCode; } @Nullable @Override public String getErrorMessage() { - return errorMessage; + return mErrorMessage; } @Nullable @Override public Exception getException() { - return exception; + return mException; } } |
