From 727da64be5c2553753c768d4b309174016968010 Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 10 Mar 2020 15:25:32 -0700 Subject: Gate stricter manifest enforcement on targetSdk R Two package parsing issues have been promoted to failures in R: a missing / tag, and an empty "android:name" attribute. The latter due to a bug in the parsing code. These need to be gated by targetSdkVersion so that APKs built for previous versions can still scan/install properly. This change introduces support for this through a framework that leverages @ChangeId to introduce individually toggle-able errors, in case a developer needs to install an app that isn't completely migrated for a new SDK version yet. The ignoreError method was removed from ParseResult as the errors it was used for were manually compared to PackageParser and it's likely they can be hard errors without breaking anything. This also adds tests for general ParseInput/Result behavior. Exempt-From-Owner-Approval: AppIntegrity already approved in older PS. Bug: 150776642 Test: atest android.content.pm.parsing.result.ParseInputAndResultTest Test: atest com.android.server.pm.parsing.PackageParsingDeferErrorTest Test: atest com.android.server.integrity.AppIntegrityManagerServiceImplTest Test: atest com.android.server.pm.parsing Change-Id: Id53a2e65f6e5e4dee9a41cc77007275b3a220ac3 --- .../content/pm/parsing/ParsingPackageUtils.java | 123 +++++++++++++----- .../pm/parsing/component/ParsedActivityUtils.java | 9 +- .../parsing/component/ParsedIntentInfoUtils.java | 15 ++- .../component/ParsedMainComponentUtils.java | 15 +-- .../content/pm/parsing/result/ParseInput.java | 65 +++++++++- .../content/pm/parsing/result/ParseResult.java | 15 +-- .../content/pm/parsing/result/ParseTypeImpl.java | 144 ++++++++++++++++----- 7 files changed, 286 insertions(+), 100 deletions(-) (limited to 'core/java/android') 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 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 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, - " does not contain an " - ); + ParseResult deferResult = input.deferError( + " does not contain an ", 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, - " does not contain an or " - ); + ParseResult deferResult = input.deferError( + " does not contain an or ", + 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, " specifies bad sharedUserId name \"" + str + "\": " + nameResult.getErrorMessage()); @@ -1166,6 +1214,20 @@ public class ParsingPackageUtils { targetCode = minCode; } + ParseResult 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 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 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 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 generateAppDetailsHiddenActivity(ParseInput input, ParsingPackage pkg) { String packageName = pkg.getPackageName(); - ParseResult taskAffinityResult = ComponentParseUtils.buildTaskAffinityName( + ParseResult 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 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 "); + } else if (value.isEmpty()) { + intentInfo.addAction(value); + // Prior to R, this was not a failure + result = input.deferError("No value supplied for ", + 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 "); + } else if (value.isEmpty()) { + intentInfo.addCategory(value); + // Prior to R, this was not a failure + result = input.deferError("No value supplied for ", + 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 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; + } + ParseResult 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) */ ParseResult 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. */ - ParseResult error(ParseResult result); + ParseResult 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,31 +17,18 @@ 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 { - /** - * 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. * 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,53 +20,132 @@ 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 { 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 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 ParseResult 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) 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 ParseResult error(int parseError) { return error(parseError, null); @@ -84,25 +163,26 @@ public class ParseTypeImpl implements ParseInput, ParseResult { } @Override - public ParseResult error(ParseResult intentResult) { - return error(intentResult.getErrorCode(), intentResult.getErrorMessage()); + public ParseResult error(ParseResult intentResult) { + return error(intentResult.getErrorCode(), intentResult.getErrorMessage(), + intentResult.getException()); } @Override public ParseResult 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 { @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 { @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; } } -- cgit v1.2.3