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 com.android.server.pm.parsing; 18 19 import android.annotation.AnyThread; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.PackageParser; 25 import android.content.pm.PackageParser.PackageParserException; 26 import android.content.pm.parsing.ParsingPackage; 27 import android.content.pm.parsing.ParsingPackageUtils; 28 import android.content.pm.parsing.ParsingUtils; 29 import android.content.pm.parsing.result.ParseInput; 30 import android.content.pm.parsing.result.ParseResult; 31 import android.content.pm.parsing.result.ParseTypeImpl; 32 import android.content.res.TypedArray; 33 import android.os.Build; 34 import android.os.ServiceManager; 35 import android.os.SystemClock; 36 import android.util.DisplayMetrics; 37 import android.util.Slog; 38 39 import com.android.internal.compat.IPlatformCompat; 40 import com.android.server.pm.PackageManagerService; 41 import com.android.server.pm.parsing.pkg.PackageImpl; 42 import com.android.server.pm.parsing.pkg.ParsedPackage; 43 44 import java.io.File; 45 46 /** 47 * The v2 of {@link PackageParser} for use when parsing is initiated in the server and must 48 * contain state contained by the server. 49 * 50 * The {@link AutoCloseable} helps signal that this class contains resources that must be freed. 51 * Although it is sufficient to release references to an instance of this class and let it get 52 * collected automatically. 53 */ 54 public class PackageParser2 implements AutoCloseable { 55 56 /** 57 * For parsing inside the system server but outside of {@link PackageManagerService}. 58 * Generally used for parsing information in an APK that hasn't been installed yet. 59 * 60 * This must be called inside the system process as it relies on {@link ServiceManager}. 61 */ 62 @NonNull forParsingFileWithDefaults()63 public static PackageParser2 forParsingFileWithDefaults() { 64 IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( 65 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); 66 return new PackageParser2(null /* separateProcesses */, false /* onlyCoreApps */, 67 null /* displayMetrics */, null /* cacheDir */, new Callback() { 68 @Override 69 public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) { 70 try { 71 return platformCompat.isChangeEnabled(changeId, appInfo); 72 } catch (Exception e) { 73 // This shouldn't happen, but assume enforcement if it does 74 Slog.wtf(ParsingUtils.TAG, "IPlatformCompat query failed", e); 75 return true; 76 } 77 } 78 79 @Override 80 public boolean hasFeature(String feature) { 81 // Assume the device doesn't support anything. This will affect permission parsing 82 // and will force <uses-permission/> declarations to include all requiredNotFeature 83 // permissions and exclude all requiredFeature permissions. This mirrors the old 84 // behavior. 85 return false; 86 } 87 }); 88 } 89 90 static final String TAG = "PackageParser2"; 91 92 private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE; 93 private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100; 94 95 private ThreadLocal<ApplicationInfo> mSharedAppInfo = 96 ThreadLocal.withInitial(() -> { 97 ApplicationInfo appInfo = new ApplicationInfo(); 98 appInfo.uid = -1; // Not a valid UID since the app will not be installed yet 99 return appInfo; 100 }); 101 102 private ThreadLocal<ParseTypeImpl> mSharedResult; 103 104 @Nullable 105 protected PackageCacher mCacher; 106 107 private ParsingPackageUtils parsingUtils; 108 109 /** 110 * @param onlyCoreApps Flag indicating this parser should only consider apps with 111 * {@code coreApp} manifest attribute to be valid apps. This is useful when 112 * creating a minimalist boot environment. 113 */ 114 public PackageParser2(String[] separateProcesses, boolean onlyCoreApps, 115 DisplayMetrics displayMetrics, @Nullable File cacheDir, @NonNull Callback callback) { 116 if (displayMetrics == null) { 117 displayMetrics = new DisplayMetrics(); 118 displayMetrics.setToDefaults(); 119 } 120 121 mCacher = cacheDir == null ? null : new PackageCacher(cacheDir); 122 123 parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics, 124 callback); 125 126 ParseInput.Callback enforcementCallback = (changeId, packageName, targetSdkVersion) -> { 127 ApplicationInfo appInfo = mSharedAppInfo.get(); 128 //noinspection ConstantConditions 129 appInfo.packageName = packageName; 130 appInfo.targetSdkVersion = targetSdkVersion; 131 return callback.isChangeEnabled(changeId, appInfo); 132 }; 133 134 mSharedResult = ThreadLocal.withInitial(() -> new ParseTypeImpl(enforcementCallback)); 135 } 136 137 /** 138 * TODO(b/135203078): Document new package parsing 139 */ 140 @AnyThread 141 public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches) 142 throws PackageParserException { 143 if (useCaches && mCacher != null) { 144 ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags); 145 if (parsed != null) { 146 return parsed; 147 } 148 } 149 150 long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0; 151 ParseInput input = mSharedResult.get().reset(); 152 ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags); 153 if (result.isError()) { 154 throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(), 155 result.getException()); 156 } 157 158 ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed(); 159 160 long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0; 161 if (mCacher != null) { 162 mCacher.cacheResult(packageFile, flags, parsed); 163 } 164 if (LOG_PARSE_TIMINGS) { 165 parseTime = cacheTime - parseTime; 166 cacheTime = SystemClock.uptimeMillis() - cacheTime; 167 if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) { 168 Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime 169 + "ms, update_cache=" + cacheTime + " ms"); 170 } 171 } 172 173 return parsed; 174 } 175 176 /** 177 * Removes the cached value for the thread the parser was created on. It is assumed that 178 * any threads created for parallel parsing will be created and released, so they don't 179 * need an explicit close call. 180 * 181 * Realistically an instance should never be retained, so when the enclosing class is released, 182 * the values will also be released, making this method unnecessary. 183 */ 184 @Override 185 public void close() { 186 mSharedResult.remove(); 187 mSharedAppInfo.remove(); 188 } 189 190 public static abstract class Callback implements ParsingPackageUtils.Callback { 191 192 @Override 193 public final ParsingPackage startParsingPackage(@NonNull String packageName, 194 @NonNull String baseCodePath, @NonNull String codePath, 195 @NonNull TypedArray manifestArray, boolean isCoreApp) { 196 return PackageImpl.forParsing(packageName, baseCodePath, codePath, manifestArray, 197 isCoreApp); 198 } 199 200 /** 201 * An indirection from {@link ParseInput.Callback#isChangeEnabled(long, String, int)}, 202 * allowing the {@link ApplicationInfo} objects to be cached in {@link #mSharedAppInfo} 203 * and cleaned up with the parser instance, not the callback instance. 204 * 205 * @param appInfo will only have 3 fields filled in, {@link ApplicationInfo#packageName}, 206 * {@link ApplicationInfo#targetSdkVersion}, and {@link ApplicationInfo#uid} 207 */ 208 public abstract boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo); 209 } 210 } 211