blob: e860c4857bf4016296e9157b4bda8ab33c0323b8 [file] [log] [blame]
Winsone23ae202020-01-24 11:56:44 -08001/*
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
17package com.android.server.pm.parsing;
18
19import android.annotation.AnyThread;
Winson727da642020-03-10 15:25:32 -070020import android.annotation.NonNull;
Winsone23ae202020-01-24 11:56:44 -080021import android.annotation.Nullable;
Winson727da642020-03-10 15:25:32 -070022import android.content.Context;
23import android.content.pm.ApplicationInfo;
Winsone23ae202020-01-24 11:56:44 -080024import android.content.pm.PackageParser;
Winsonf00c7552020-01-28 12:52:01 -080025import android.content.pm.PackageParser.PackageParserException;
Winsone23ae202020-01-24 11:56:44 -080026import android.content.pm.parsing.ParsingPackage;
27import android.content.pm.parsing.ParsingPackageUtils;
Winsonf00c7552020-01-28 12:52:01 -080028import android.content.pm.parsing.result.ParseInput;
29import android.content.pm.parsing.result.ParseResult;
30import android.content.pm.parsing.result.ParseTypeImpl;
Winsone23ae202020-01-24 11:56:44 -080031import android.content.res.TypedArray;
32import android.os.Build;
Winson727da642020-03-10 15:25:32 -070033import android.os.ServiceManager;
Winsone23ae202020-01-24 11:56:44 -080034import android.os.SystemClock;
35import android.util.DisplayMetrics;
36import android.util.Slog;
37
Winson727da642020-03-10 15:25:32 -070038import com.android.server.compat.PlatformCompat;
39import com.android.server.pm.PackageManagerService;
Winsone23ae202020-01-24 11:56:44 -080040import com.android.server.pm.parsing.pkg.PackageImpl;
41import com.android.server.pm.parsing.pkg.ParsedPackage;
42
43import java.io.File;
44
45/**
46 * The v2 of {@link PackageParser} for use when parsing is initiated in the server and must
47 * contain state contained by the server.
Winson727da642020-03-10 15:25:32 -070048 *
49 * The {@link AutoCloseable} helps signal that this class contains resources that must be freed.
50 * Although it is sufficient to release references to an instance of this class and let it get
51 * collected automatically.
Winsone23ae202020-01-24 11:56:44 -080052 */
Winson727da642020-03-10 15:25:32 -070053public class PackageParser2 implements AutoCloseable {
54
55 /**
56 * For parsing inside the system server but outside of {@link PackageManagerService}.
57 * Generally used for parsing information in an APK that hasn't been installed yet.
58 *
59 * This must be called inside the system process as it relies on {@link ServiceManager}.
60 */
61 public static PackageParser2 forParsingFileWithDefaults() {
62 PlatformCompat platformCompat =
63 (PlatformCompat) ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
64 return new PackageParser2(null /* separateProcesses */, false /* onlyCoreApps */,
65 null /* displayMetrics */, null /* cacheDir */, new Callback() {
66 @Override
67 public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
68 return platformCompat.isChangeEnabled(changeId, appInfo);
69 }
70
71 @Override
72 public boolean hasFeature(String feature) {
73 // Assume the device doesn't support anything. This will affect permission parsing
74 // and will force <uses-permission/> declarations to include all requiredNotFeature
75 // permissions and exclude all requiredFeature permissions. This mirrors the old
76 // behavior.
77 return false;
78 }
79 });
80 }
Winsone23ae202020-01-24 11:56:44 -080081
Makoto Onukidb91e182020-02-21 10:28:08 -080082 static final String TAG = "PackageParser2";
Winsone23ae202020-01-24 11:56:44 -080083
84 private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
85 private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
86
Winson727da642020-03-10 15:25:32 -070087 private ThreadLocal<ApplicationInfo> mSharedAppInfo =
88 ThreadLocal.withInitial(() -> {
89 ApplicationInfo appInfo = new ApplicationInfo();
90 appInfo.uid = -1; // Not a valid UID since the app will not be installed yet
91 return appInfo;
92 });
Winsone23ae202020-01-24 11:56:44 -080093
Winson727da642020-03-10 15:25:32 -070094 private ThreadLocal<ParseTypeImpl> mSharedResult;
Winsone23ae202020-01-24 11:56:44 -080095
96 @Nullable
97 protected PackageCacher mCacher;
98
Winsonf00c7552020-01-28 12:52:01 -080099 private ParsingPackageUtils parsingUtils;
100
Winsone23ae202020-01-24 11:56:44 -0800101 /**
102 * @param onlyCoreApps Flag indicating this parser should only consider apps with
103 * {@code coreApp} manifest attribute to be valid apps. This is useful when
104 * creating a minimalist boot environment.
105 */
106 public PackageParser2(String[] separateProcesses, boolean onlyCoreApps,
Winson727da642020-03-10 15:25:32 -0700107 DisplayMetrics displayMetrics, @Nullable File cacheDir, @NonNull Callback callback) {
Winsone23ae202020-01-24 11:56:44 -0800108 if (displayMetrics == null) {
Winson727da642020-03-10 15:25:32 -0700109 displayMetrics = new DisplayMetrics();
110 displayMetrics.setToDefaults();
Winsone23ae202020-01-24 11:56:44 -0800111 }
112
113 mCacher = cacheDir == null ? null : new PackageCacher(cacheDir);
Winson727da642020-03-10 15:25:32 -0700114
115 parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics,
116 callback);
117
118 ParseInput.Callback enforcementCallback = (changeId, packageName, targetSdkVersion) -> {
119 ApplicationInfo appInfo = mSharedAppInfo.get();
120 //noinspection ConstantConditions
121 appInfo.packageName = packageName;
122 appInfo.targetSdkVersion = targetSdkVersion;
123 return callback.isChangeEnabled(changeId, appInfo);
Winsone23ae202020-01-24 11:56:44 -0800124 };
Winsonf00c7552020-01-28 12:52:01 -0800125
Winson727da642020-03-10 15:25:32 -0700126 mSharedResult = ThreadLocal.withInitial(() -> new ParseTypeImpl(enforcementCallback));
Winsone23ae202020-01-24 11:56:44 -0800127 }
128
129 /**
130 * TODO(b/135203078): Document new package parsing
131 */
132 @AnyThread
133 public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
Winsonf00c7552020-01-28 12:52:01 -0800134 throws PackageParserException {
Winsone23ae202020-01-24 11:56:44 -0800135 if (useCaches && mCacher != null) {
136 ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
137 if (parsed != null) {
138 return parsed;
139 }
140 }
141
142 long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
Winsonf00c7552020-01-28 12:52:01 -0800143 ParseInput input = mSharedResult.get().reset();
144 ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
145 if (result.isError()) {
146 throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
147 result.getException());
148 }
149
150 ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
Winsone23ae202020-01-24 11:56:44 -0800151
152 long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
153 if (mCacher != null) {
154 mCacher.cacheResult(packageFile, flags, parsed);
155 }
156 if (LOG_PARSE_TIMINGS) {
157 parseTime = cacheTime - parseTime;
158 cacheTime = SystemClock.uptimeMillis() - cacheTime;
159 if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
160 Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
161 + "ms, update_cache=" + cacheTime + " ms");
162 }
163 }
164
165 return parsed;
166 }
167
Winson727da642020-03-10 15:25:32 -0700168 /**
169 * Removes the cached value for the thread the parser was created on. It is assumed that
170 * any threads created for parallel parsing will be created and released, so they don't
171 * need an explicit close call.
172 *
173 * Realistically an instance should never be retained, so when the enclosing class is released,
174 * the values will also be released, making this method unnecessary.
175 */
176 @Override
177 public void close() {
178 mSharedResult.remove();
179 mSharedAppInfo.remove();
180 }
181
Winsone23ae202020-01-24 11:56:44 -0800182 public static abstract class Callback implements ParsingPackageUtils.Callback {
183
184 @Override
Winson727da642020-03-10 15:25:32 -0700185 public final ParsingPackage startParsingPackage(@NonNull String packageName,
186 @NonNull String baseCodePath, @NonNull String codePath,
187 @NonNull TypedArray manifestArray, boolean isCoreApp) {
Winsone23ae202020-01-24 11:56:44 -0800188 return PackageImpl.forParsing(packageName, baseCodePath, codePath, manifestArray,
189 isCoreApp);
190 }
Winson727da642020-03-10 15:25:32 -0700191
192 /**
193 * An indirection from {@link ParseInput.Callback#isChangeEnabled(long, String, int)},
194 * allowing the {@link ApplicationInfo} objects to be cached in {@link #mSharedAppInfo}
195 * and cleaned up with the parser instance, not the callback instance.
196 *
197 * @param appInfo will only have 3 fields filled in, {@link ApplicationInfo#packageName},
198 * {@link ApplicationInfo#targetSdkVersion}, and {@link ApplicationInfo#uid}
199 */
200 public abstract boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo);
Winsone23ae202020-01-24 11:56:44 -0800201 }
202}