1 /* 2 * Copyright (C) 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 17 package android.content.pm.parsing.result; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.ChangeId; 23 import android.compat.annotation.EnabledAfter; 24 import android.content.pm.PackageManager; 25 import android.os.Build; 26 27 /** 28 * Used as a method parameter which is then transformed into a {@link ParseResult}. This is 29 * generalized as it doesn't matter what type this input is for. It's simply to hide the 30 * methods of {@link ParseResult}. 31 * 32 * @hide 33 */ 34 public interface ParseInput { 35 36 /** 37 * Errors encountered during parsing may rely on the targetSDK version of the application to 38 * determine whether or not to fail. These are passed into {@link #deferError(String, long)} 39 * when encountered, and the implementation will handle how to defer the errors until the 40 * targetSdkVersion is known and sent to {@link #enableDeferredError(String, int)}. 41 * 42 * All of these must be marked {@link ChangeId}, as that is the mechanism used to check if the 43 * error must be propagated. This framework also allows developers to pre-disable specific 44 * checks if they wish to target a newer SDK version in a development environment without 45 * having to migrate their entire app to validate on a newer platform. 46 */ 47 final class DeferredError { 48 /** 49 * Missing an "application" or "instrumentation" tag. 50 */ 51 @ChangeId 52 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) 53 public static final long MISSING_APP_TAG = 150776642; 54 55 /** 56 * An intent filter's actor or category is an empty string. A bug in the platform before R 57 * allowed this to pass through without an error. This does not include cases when the 58 * attribute is null/missing, as that has always been a failure. 59 */ 60 @ChangeId 61 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) 62 public static final long EMPTY_INTENT_ACTION_CATEGORY = 151163173; 63 64 /** 65 * The {@code resources.arsc} of one of the APKs being installed is compressed or not 66 * aligned on a 4-byte boundary. Resource tables that cannot be memory mapped exert excess 67 * memory pressure on the system and drastically slow down construction of 68 * {@link android.content.res.Resources} objects. 69 */ 70 @ChangeId 71 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) 72 public static final long RESOURCES_ARSC_COMPRESSED = 132742131; 73 74 /** 75 * TODO(chiuwinson): This is required because PackageManager#getPackageArchiveInfo 76 * cannot read the targetSdk info from the changeId because it requires the 77 * READ_COMPAT_CHANGE_CONFIG which cannot be obtained automatically without entering the 78 * server process. This should be removed once an alternative is found, or if the API 79 * is removed. 80 * @return the targetSdk that this change is gated on (> check), or -1 if disabled 81 */ 82 @IntRange(from = -1, to = Integer.MAX_VALUE) getTargetSdkForChange(long changeId)83 public static int getTargetSdkForChange(long changeId) { 84 if (changeId == MISSING_APP_TAG 85 || changeId == EMPTY_INTENT_ACTION_CATEGORY 86 || changeId == RESOURCES_ARSC_COMPRESSED) { 87 return Build.VERSION_CODES.Q; 88 } 89 90 return -1; 91 } 92 } 93 success(ResultType result)94 <ResultType> ParseResult<ResultType> success(ResultType result); 95 96 /** 97 * Used for errors gated by {@link DeferredError}. Will return an error result if the 98 * targetSdkVersion is already known and this must be returned as a real error. The result 99 * contains null and should not be unwrapped. 100 * 101 * @see #error(String) 102 */ deferError(@onNull String parseError, long deferredError)103 ParseResult<?> deferError(@NonNull String parseError, long deferredError); 104 105 /** 106 * Called after targetSdkVersion is known. Returns an error result if a previously deferred 107 * error was registered. The result contains null and should not be unwrapped. 108 */ enableDeferredError(String packageName, int targetSdkVersion)109 ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion); 110 111 /** 112 * This will assign errorCode to {@link PackageManager#INSTALL_PARSE_FAILED_SKIPPED, used for 113 * packages which should be ignored by the caller. 114 * 115 * @see #error(int, String, Exception) 116 */ skip(@onNull String parseError)117 <ResultType> ParseResult<ResultType> skip(@NonNull String parseError); 118 119 /** @see #error(int, String, Exception) */ error(int parseError)120 <ResultType> ParseResult<ResultType> error(int parseError); 121 122 /** 123 * This will assign errorCode to {@link PackageManager#INSTALL_PARSE_FAILED_MANIFEST_MALFORMED}. 124 * @see #error(int, String, Exception) 125 */ error(@onNull String parseError)126 <ResultType> ParseResult<ResultType> error(@NonNull String parseError); 127 128 /** @see #error(int, String, Exception) */ error(int parseError, @Nullable String errorMessage)129 <ResultType> ParseResult<ResultType> error(int parseError, @Nullable String errorMessage); 130 131 /** 132 * Marks this as an error result. When this method is called, the return value <b>must</b> 133 * be returned to the exit of the parent method that took in this {@link ParseInput} as a 134 * parameter. 135 * 136 * The calling site of that method is then expected to check the result for error, and 137 * continue to bubble up if it is an error. 138 * 139 * If the result {@link ParseResult#isSuccess()}, then it can be used as-is, as 140 * overlapping/consecutive successes are allowed. 141 */ error(int parseError, @Nullable String errorMessage, @Nullable Exception exception)142 <ResultType> ParseResult<ResultType> error(int parseError, @Nullable String errorMessage, 143 @Nullable Exception exception); 144 145 /** 146 * Moves the error in {@param result} to this input's type. In practice this does nothing 147 * but cast the type of the {@link ParseResult} for type safety, since the parameter 148 * and the receiver should be the same object. 149 */ error(ParseResult<?> result)150 <ResultType> ParseResult<ResultType> error(ParseResult<?> result); 151 152 /** 153 * Implemented instead of a direct reference to 154 * {@link com.android.internal.compat.IPlatformCompat}, allowing caching and testing logic to 155 * be separated out. 156 */ 157 interface Callback { 158 /** 159 * @return true if the changeId should be enabled 160 */ isChangeEnabled(long changeId, @NonNull String packageName, int targetSdkVersion)161 boolean isChangeEnabled(long changeId, @NonNull String packageName, int targetSdkVersion); 162 } 163 } 164