• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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