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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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.
|
||||
*
|
||||
|
||||
@@ -20,51 +20,130 @@ 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public ParseInput reset() {
|
||||
this.result = null;
|
||||
this.errorCode = PackageManager.INSTALL_SUCCEEDED;
|
||||
this.errorMessage = null;
|
||||
this.exception = null;
|
||||
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 void ignoreError() {
|
||||
reset();
|
||||
public <ResultType> ParseResult<ResultType> success(ResultType result) {
|
||||
if (mErrorCode != PackageManager.INSTALL_SUCCEEDED) {
|
||||
Slog.wtf(ParsingUtils.TAG, "Cannot set to success after set to error, was "
|
||||
+ mErrorMessage, mException);
|
||||
}
|
||||
mResult = result;
|
||||
//noinspection unchecked
|
||||
return (ParseResult<ResultType>) 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);
|
||||
public ParseResult<?> deferError(@NonNull String parseError, long deferredError) {
|
||||
if (DEBUG_THROW_ALL_ERRORS) {
|
||||
return error(parseError);
|
||||
}
|
||||
this.result = result;
|
||||
//noinspection unchecked
|
||||
return (ParseResult<ResultType>) this;
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user