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