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.internal.content.om; 18 19 import static com.android.internal.content.om.OverlayConfig.TAG; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.pm.parsing.ApkLite; 24 import android.content.pm.parsing.ApkLiteParseUtils; 25 import android.content.pm.parsing.FrameworkParsingPackageUtils; 26 import android.content.pm.parsing.result.ParseResult; 27 import android.content.pm.parsing.result.ParseTypeImpl; 28 import android.text.TextUtils; 29 import android.util.ArrayMap; 30 import android.util.Log; 31 import android.util.Pair; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.io.File; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.List; 39 40 /** 41 * This class scans a directory containing overlay APKs and extracts information from the overlay 42 * manifests by parsing the overlay manifests. 43 */ 44 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 45 public class OverlayScanner { 46 47 /** Represents information parsed from the manifest of an overlay. */ 48 public static class ParsedOverlayInfo { 49 public final String packageName; 50 public final String targetPackageName; 51 public final int targetSdkVersion; 52 public final boolean isStatic; 53 public final int priority; 54 public final File path; 55 @Nullable public final File preInstalledApexPath; 56 ParsedOverlayInfo(String packageName, String targetPackageName, int targetSdkVersion, boolean isStatic, int priority, File path, @Nullable File preInstalledApexPath)57 public ParsedOverlayInfo(String packageName, String targetPackageName, 58 int targetSdkVersion, boolean isStatic, int priority, File path, 59 @Nullable File preInstalledApexPath) { 60 this.packageName = packageName; 61 this.targetPackageName = targetPackageName; 62 this.targetSdkVersion = targetSdkVersion; 63 this.isStatic = isStatic; 64 this.priority = priority; 65 this.path = path; 66 this.preInstalledApexPath = preInstalledApexPath; 67 } 68 69 @Override toString()70 public String toString() { 71 return getClass().getSimpleName() + String.format("{packageName=%s" 72 + ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s" 73 + ", priority=%s, path=%s, preInstalledApexPath=%s}", 74 packageName, targetPackageName, targetSdkVersion, isStatic, 75 priority, path, preInstalledApexPath); 76 } 77 78 /** 79 * Retrieves the path of the overlay in its original installation partition. 80 * 81 * An Overlay in an APEX, which is an update of an APEX in a given partition, 82 * is considered as belonging to that partition. 83 */ 84 @NonNull getOriginalPartitionPath()85 public File getOriginalPartitionPath() { 86 return preInstalledApexPath != null ? preInstalledApexPath : path; 87 } 88 } 89 90 /** 91 * A map of overlay package name to the parsed manifest information of the latest version of 92 * the overlay. 93 */ 94 private final ArrayMap<String, ParsedOverlayInfo> mParsedOverlayInfos = new ArrayMap<>(); 95 96 /** 97 * A list of pair<packageName, apkFile> which is excluded from the system based on the 98 * system property condition. 99 * 100 * @see #isExcludedOverlayPackage(String, OverlayConfigParser.OverlayPartition) 101 */ 102 private final List<Pair<String, File>> mExcludedOverlayPackages = new ArrayList<>(); 103 104 /** Retrieves information parsed from the overlay with the package name. */ 105 @Nullable getParsedInfo(String packageName)106 public final ParsedOverlayInfo getParsedInfo(String packageName) { 107 return mParsedOverlayInfos.get(packageName); 108 } 109 110 /** Retrieves all of the scanned overlays. */ 111 @NonNull getAllParsedInfos()112 final Collection<ParsedOverlayInfo> getAllParsedInfos() { 113 return mParsedOverlayInfos.values(); 114 } 115 116 /** 117 * Returns {@code true} if the given package name on the given overlay partition is an 118 * excluded overlay package. 119 * <p> 120 * An excluded overlay package declares overlay attributes of required system property in its 121 * manifest that do not match the corresponding values on the device. 122 */ isExcludedOverlayPackage(@onNull String packageName, @NonNull OverlayConfigParser.OverlayPartition overlayPartition)123 final boolean isExcludedOverlayPackage(@NonNull String packageName, 124 @NonNull OverlayConfigParser.OverlayPartition overlayPartition) { 125 for (int i = 0; i < mExcludedOverlayPackages.size(); i++) { 126 final Pair<String, File> pair = mExcludedOverlayPackages.get(i); 127 if (pair.first.equals(packageName) 128 && overlayPartition.containsOverlay(pair.second)) { 129 return true; 130 } 131 } 132 return false; 133 } 134 135 /** 136 * Recursively searches the directory for overlay APKs. If an overlay is found with the same 137 * package name as a previously scanned overlay, the info of the new overlay will replace the 138 * info of the previously scanned overlay. 139 */ scanDir(File partitionOverlayDir)140 public void scanDir(File partitionOverlayDir) { 141 if (!partitionOverlayDir.exists() || !partitionOverlayDir.isDirectory()) { 142 return; 143 } 144 145 if (!partitionOverlayDir.canRead()) { 146 Log.w(TAG, "Directory " + partitionOverlayDir + " cannot be read"); 147 return; 148 } 149 150 final File[] files = partitionOverlayDir.listFiles(); 151 if (files == null) { 152 return; 153 } 154 155 for (int i = 0; i < files.length; i++) { 156 final File f = files[i]; 157 if (f.isDirectory()) { 158 scanDir(f); 159 } 160 161 if (!f.isFile() || !f.getPath().endsWith(".apk")) { 162 continue; 163 } 164 165 final ParsedOverlayInfo info = parseOverlayManifest(f, mExcludedOverlayPackages); 166 if (info == null) { 167 continue; 168 } 169 170 mParsedOverlayInfos.put(info.packageName, info); 171 } 172 } 173 174 /** 175 * Extracts information about the overlay from its manifest. Adds the package name and apk file 176 * into the {@code outExcludedOverlayPackages} if the apk is excluded from the system based 177 * on the system property condition. 178 */ 179 @VisibleForTesting parseOverlayManifest(File overlayApk, List<Pair<String, File>> outExcludedOverlayPackages)180 public ParsedOverlayInfo parseOverlayManifest(File overlayApk, 181 List<Pair<String, File>> outExcludedOverlayPackages) { 182 final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat(); 183 final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(), 184 overlayApk, 185 FrameworkParsingPackageUtils.PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY); 186 if (ret.isError()) { 187 Log.w(TAG, "Got exception loading overlay.", ret.getException()); 188 return null; 189 } 190 final ApkLite apkLite = ret.getResult(); 191 if (apkLite.getTargetPackageName() == null) { 192 // Not an overlay package 193 return null; 194 } 195 final String propName = apkLite.getRequiredSystemPropertyName(); 196 final String propValue = apkLite.getRequiredSystemPropertyValue(); 197 if ((!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) 198 && !FrameworkParsingPackageUtils.checkRequiredSystemProperties(propName, 199 propValue)) { 200 // The overlay package should be excluded. Adds it into the outExcludedOverlayPackages 201 // for overlay configuration parser to ignore it. 202 outExcludedOverlayPackages.add(Pair.create(apkLite.getPackageName(), overlayApk)); 203 return null; 204 } 205 return new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(), 206 apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(), 207 apkLite.getOverlayPriority(), new File(apkLite.getPath()), null); 208 } 209 } 210