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