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.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageManager; 24 import android.os.ServiceManager; 25 import android.util.ArrayMap; 26 import android.util.Log; 27 import android.util.Slog; 28 29 import com.android.internal.compat.IPlatformCompat; 30 import com.android.internal.util.CollectionUtils; 31 32 /** @hide */ 33 public class ParseTypeImpl implements ParseInput, ParseResult<Object> { 34 35 private static final String TAG = "ParseTypeImpl"; 36 37 public static final boolean DEBUG_FILL_STACK_TRACE = false; 38 39 public static final boolean DEBUG_LOG_ON_ERROR = false; 40 41 public static final boolean DEBUG_THROW_ALL_ERRORS = false; 42 43 @NonNull 44 private final Callback mCallback; 45 46 private Object mResult; 47 48 private int mErrorCode = PackageManager.INSTALL_SUCCEEDED; 49 50 @Nullable 51 private String mErrorMessage; 52 53 @Nullable 54 private Exception mException; 55 56 /** 57 * Errors encountered before targetSdkVersion is known. 58 * The size upper bound is the number of longs in {@link DeferredError} 59 */ 60 @Nullable 61 private ArrayMap<Long, String> mDeferredErrors = null; 62 63 private String mPackageName; 64 private int mTargetSdkVersion = -1; 65 66 /** 67 * Specifically for {@link PackageManager#getPackageArchiveInfo(String, int)} where 68 * {@link IPlatformCompat} cannot be used because the cross-package READ_COMPAT_CHANGE_CONFIG 69 * permission cannot be obtained. 70 */ forParsingWithoutPlatformCompat()71 public static ParseTypeImpl forParsingWithoutPlatformCompat() { 72 return new ParseTypeImpl((changeId, packageName, targetSdkVersion) -> { 73 int gateSdkVersion = DeferredError.getTargetSdkForChange(changeId); 74 if (gateSdkVersion == -1) { 75 return false; 76 } 77 return targetSdkVersion > gateSdkVersion; 78 }); 79 } 80 81 /** 82 * Assumes {@link Context#PLATFORM_COMPAT_SERVICE} is available to the caller. For use 83 * with {@link android.content.pm.parsing.ApkLiteParseUtils} or similar where parsing is 84 * done outside of {@link com.android.server.pm.PackageManagerService}. 85 */ forDefaultParsing()86 public static ParseTypeImpl forDefaultParsing() { 87 IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( 88 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 89 return new ParseTypeImpl((changeId, packageName, targetSdkVersion) -> { 90 ApplicationInfo appInfo = new ApplicationInfo(); 91 appInfo.packageName = packageName; 92 appInfo.targetSdkVersion = targetSdkVersion; 93 try { 94 return platformCompat.isChangeEnabled(changeId, appInfo); 95 } catch (Exception e) { 96 // This shouldn't happen, but assume enforcement if it does 97 Slog.wtf(TAG, "IPlatformCompat query failed", e); 98 return true; 99 } 100 }); 101 } 102 103 /** 104 * @param callback if nullable, fallback to manual targetSdk > Q check 105 */ 106 public ParseTypeImpl(@NonNull Callback callback) { 107 mCallback = callback; 108 } 109 110 public ParseInput reset() { 111 mResult = null; 112 mErrorCode = PackageManager.INSTALL_SUCCEEDED; 113 mErrorMessage = null; 114 mException = null; 115 if (mDeferredErrors != null) { 116 // If the memory was already allocated, don't bother freeing and re-allocating, 117 // as this could occur hundreds of times depending on what the caller is doing and 118 // how many APKs they're going through. 119 mDeferredErrors.erase(); 120 } 121 mTargetSdkVersion = -1; 122 return this; 123 } 124 125 @Override 126 public <ResultType> ParseResult<ResultType> success(ResultType result) { 127 if (mErrorCode != PackageManager.INSTALL_SUCCEEDED) { 128 Slog.wtf(TAG, "Cannot set to success after set to error, was " 129 + mErrorMessage, mException); 130 } 131 mResult = result; 132 //noinspection unchecked 133 return (ParseResult<ResultType>) this; 134 } 135 136 @Override 137 public ParseResult<?> deferError(@NonNull String parseError, long deferredError) { 138 if (DEBUG_THROW_ALL_ERRORS) { 139 return error(parseError); 140 } 141 if (mTargetSdkVersion != -1) { 142 if (mDeferredErrors != null && mDeferredErrors.containsKey(deferredError)) { 143 // If the map already contains the key, that means it's already been checked and 144 // found to be disabled. Otherwise it would've failed when mTargetSdkVersion was 145 // set to non-null. 146 return success(null); 147 } 148 149 if (mCallback.isChangeEnabled(deferredError, mPackageName, mTargetSdkVersion)) { 150 return error(parseError); 151 } else { 152 if (mDeferredErrors == null) { 153 mDeferredErrors = new ArrayMap<>(); 154 } 155 mDeferredErrors.put(deferredError, null); 156 return success(null); 157 } 158 } 159 160 if (mDeferredErrors == null) { 161 mDeferredErrors = new ArrayMap<>(); 162 } 163 164 // Only save the first occurrence of any particular error 165 mDeferredErrors.putIfAbsent(deferredError, parseError); 166 return success(null); 167 } 168 169 @Override 170 public ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion) { 171 mPackageName = packageName; 172 mTargetSdkVersion = targetSdkVersion; 173 174 int size = CollectionUtils.size(mDeferredErrors); 175 for (int index = size - 1; index >= 0; index--) { 176 long changeId = mDeferredErrors.keyAt(index); 177 String errorMessage = mDeferredErrors.valueAt(index); 178 if (mCallback.isChangeEnabled(changeId, mPackageName, mTargetSdkVersion)) { 179 return error(errorMessage); 180 } else { 181 // No point holding onto the string, but need to maintain the key to signal 182 // that the error was checked with isChangeEnabled and found to be disabled. 183 mDeferredErrors.setValueAt(index, null); 184 } 185 } 186 187 return success(null); 188 } 189 190 @Override 191 public <ResultType> ParseResult<ResultType> skip(@NonNull String parseError) { 192 return error(PackageManager.INSTALL_PARSE_FAILED_SKIPPED, parseError); 193 } 194 195 @Override 196 public <ResultType> ParseResult<ResultType> error(int parseError) { 197 return error(parseError, null); 198 } 199 200 @Override 201 public <ResultType> ParseResult<ResultType> error(@NonNull String parseError) { 202 return error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, parseError); 203 } 204 205 @Override 206 public <ResultType> ParseResult<ResultType> error(int errorCode, 207 @Nullable String errorMessage) { 208 return error(errorCode, errorMessage, null); 209 } 210 211 @Override 212 public <ResultType> ParseResult<ResultType> error(ParseResult<?> intentResult) { 213 return error(intentResult.getErrorCode(), intentResult.getErrorMessage(), 214 intentResult.getException()); 215 } 216 217 @Override 218 public <ResultType> ParseResult<ResultType> error(int errorCode, @Nullable String errorMessage, 219 Exception exception) { 220 mErrorCode = errorCode; 221 mErrorMessage = errorMessage; 222 mException = exception; 223 224 if (DEBUG_FILL_STACK_TRACE) { 225 if (exception == null) { 226 mException = new Exception(); 227 } 228 } 229 230 if (DEBUG_LOG_ON_ERROR) { 231 Exception exceptionToLog = mException != null ? mException : new Exception(); 232 Log.w(TAG, "ParseInput set to error " + errorCode + ", " + errorMessage, 233 exceptionToLog); 234 } 235 236 //noinspection unchecked 237 return (ParseResult<ResultType>) this; 238 } 239 240 @Override 241 public Object getResult() { 242 return mResult; 243 } 244 245 @Override 246 public boolean isSuccess() { 247 return mErrorCode == PackageManager.INSTALL_SUCCEEDED; 248 } 249 250 @Override 251 public boolean isError() { 252 return !isSuccess(); 253 } 254 255 @Override 256 public int getErrorCode() { 257 return mErrorCode; 258 } 259 260 @Nullable 261 @Override 262 public String getErrorMessage() { 263 return mErrorMessage; 264 } 265 266 @Nullable 267 @Override 268 public Exception getException() { 269 return mException; 270 } 271 } 272