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; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.os.Build; 23 import android.os.Build.Partition; 24 import android.os.Environment; 25 import android.os.FileUtils; 26 import android.os.SystemProperties; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.function.Function; 37 38 /** 39 * Exposes {@link #SYSTEM_PARTITIONS} which represents the partitions in which application packages 40 * can be installed. The partitions are ordered from most generic (lowest priority) to most specific 41 * (greatest priority). 42 * 43 * @hide 44 **/ 45 public class PackagePartitions { 46 public static final int PARTITION_SYSTEM = 0; 47 public static final int PARTITION_VENDOR = 1; 48 public static final int PARTITION_ODM = 2; 49 public static final int PARTITION_OEM = 3; 50 public static final int PARTITION_PRODUCT = 4; 51 public static final int PARTITION_SYSTEM_EXT = 5; 52 53 @IntDef(prefix = { "PARTITION_" }, value = { 54 PARTITION_SYSTEM, 55 PARTITION_VENDOR, 56 PARTITION_ODM, 57 PARTITION_OEM, 58 PARTITION_PRODUCT, 59 PARTITION_SYSTEM_EXT 60 }) 61 @Retention(RetentionPolicy.SOURCE) 62 public @interface PartitionType {} 63 64 /** 65 * The list of all system partitions that may contain packages in ascending order of 66 * specificity (the more generic, the earlier in the list a partition appears). 67 */ 68 private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS = 69 new ArrayList<>(Arrays.asList( 70 new SystemPartition(Environment.getRootDirectory(), 71 PARTITION_SYSTEM, Partition.PARTITION_NAME_SYSTEM, 72 true /* containsPrivApp */, false /* containsOverlay */), 73 new SystemPartition(Environment.getVendorDirectory(), 74 PARTITION_VENDOR, Partition.PARTITION_NAME_VENDOR, 75 true /* containsPrivApp */, true /* containsOverlay */), 76 new SystemPartition(Environment.getOdmDirectory(), 77 PARTITION_ODM, Partition.PARTITION_NAME_ODM, 78 true /* containsPrivApp */, true /* containsOverlay */), 79 new SystemPartition(Environment.getOemDirectory(), 80 PARTITION_OEM, Partition.PARTITION_NAME_OEM, 81 false /* containsPrivApp */, true /* containsOverlay */), 82 new SystemPartition(Environment.getProductDirectory(), 83 PARTITION_PRODUCT, Partition.PARTITION_NAME_PRODUCT, 84 true /* containsPrivApp */, true /* containsOverlay */), 85 new SystemPartition(Environment.getSystemExtDirectory(), 86 PARTITION_SYSTEM_EXT, Partition.PARTITION_NAME_SYSTEM_EXT, 87 true /* containsPrivApp */, true /* containsOverlay */))); 88 89 /** 90 * A string to represent the fingerprint of this build and all package partitions. Using it to 91 * determine whether the system update has occurred. Different from {@link Build#FINGERPRINT}, 92 * this string is digested from the fingerprints of the build and all package partitions to 93 * help detect the partition update. 94 */ 95 public static final String FINGERPRINT = getFingerprint(); 96 97 /** 98 * Returns a list in which the elements are products of the specified function applied to the 99 * list of {@link #SYSTEM_PARTITIONS} in increasing specificity order. 100 */ getOrderedPartitions( @onNull Function<SystemPartition, T> producer)101 public static <T> ArrayList<T> getOrderedPartitions( 102 @NonNull Function<SystemPartition, T> producer) { 103 final ArrayList<T> out = new ArrayList<>(); 104 for (int i = 0, n = SYSTEM_PARTITIONS.size(); i < n; i++) { 105 final T v = producer.apply(SYSTEM_PARTITIONS.get(i)); 106 if (v != null) { 107 out.add(v); 108 } 109 } 110 return out; 111 } 112 canonicalize(File path)113 private static File canonicalize(File path) { 114 try { 115 return path.getCanonicalFile(); 116 } catch (IOException e) { 117 return path; 118 } 119 } 120 121 /** 122 * Returns a fingerprint string for this build and all package partitions. The string is 123 * digested from the fingerprints of the build and all package partitions. 124 * 125 * @return A string to represent the fingerprint of this build and all package partitions. 126 */ 127 @NonNull getFingerprint()128 private static String getFingerprint() { 129 final String[] digestProperties = new String[SYSTEM_PARTITIONS.size() + 1]; 130 for (int i = 0; i < SYSTEM_PARTITIONS.size(); i++) { 131 final String partitionName = SYSTEM_PARTITIONS.get(i).getName(); 132 digestProperties[i] = "ro." + partitionName + ".build.fingerprint"; 133 } 134 digestProperties[SYSTEM_PARTITIONS.size()] = "ro.build.fingerprint"; // build fingerprint 135 return SystemProperties.digestOf(digestProperties); 136 } 137 138 /** Represents a partition that contains application packages. */ 139 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) 140 public static class SystemPartition { 141 @PartitionType 142 public final int type; 143 144 @NonNull 145 private final String mName; 146 147 @NonNull 148 private final DeferredCanonicalFile mFolder; 149 150 @Nullable 151 private final DeferredCanonicalFile mAppFolder; 152 153 @Nullable 154 private final DeferredCanonicalFile mPrivAppFolder; 155 156 @Nullable 157 private final DeferredCanonicalFile mOverlayFolder; 158 159 @NonNull 160 private final File mNonConicalFolder; 161 SystemPartition(@onNull File folder, @PartitionType int type, String name, boolean containsPrivApp, boolean containsOverlay)162 private SystemPartition(@NonNull File folder, @PartitionType int type, String name, 163 boolean containsPrivApp, boolean containsOverlay) { 164 this.type = type; 165 this.mName = name; 166 this.mFolder = new DeferredCanonicalFile(folder); 167 this.mAppFolder = new DeferredCanonicalFile(folder, "app"); 168 this.mPrivAppFolder = containsPrivApp ? new DeferredCanonicalFile(folder, "priv-app") 169 : null; 170 this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay") 171 : null; 172 this.mNonConicalFolder = folder; 173 } 174 SystemPartition(@onNull SystemPartition original)175 public SystemPartition(@NonNull SystemPartition original) { 176 this.type = original.type; 177 this.mName = original.mName; 178 this.mFolder = new DeferredCanonicalFile(original.mFolder.getFile()); 179 this.mAppFolder = original.mAppFolder; 180 this.mPrivAppFolder = original.mPrivAppFolder; 181 this.mOverlayFolder = original.mOverlayFolder; 182 this.mNonConicalFolder = original.mNonConicalFolder; 183 } 184 185 /** 186 * Creates a partition containing the same folders as the original partition but with a 187 * different root folder. 188 */ SystemPartition(@onNull File rootFolder, @NonNull SystemPartition partition)189 public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) { 190 this(rootFolder, partition.type, partition.mName, partition.mPrivAppFolder != null, 191 partition.mOverlayFolder != null); 192 } 193 194 /** 195 * Returns the name identifying the partition. 196 * @see Partition 197 */ 198 @NonNull getName()199 public String getName() { 200 return mName; 201 } 202 203 /** Returns the canonical folder of the partition. */ 204 @NonNull getFolder()205 public File getFolder() { 206 return mFolder.getFile(); 207 } 208 209 /** Returns the non-canonical folder of the partition. */ 210 @NonNull getNonConicalFolder()211 public File getNonConicalFolder() { 212 return mNonConicalFolder; 213 } 214 215 /** Returns the canonical app folder of the partition. */ 216 @Nullable getAppFolder()217 public File getAppFolder() { 218 return mAppFolder == null ? null : mAppFolder.getFile(); 219 } 220 221 /** Returns the canonical priv-app folder of the partition, if one exists. */ 222 @Nullable getPrivAppFolder()223 public File getPrivAppFolder() { 224 return mPrivAppFolder == null ? null : mPrivAppFolder.getFile(); 225 } 226 227 /** Returns the canonical overlay folder of the partition, if one exists. */ 228 @Nullable getOverlayFolder()229 public File getOverlayFolder() { 230 return mOverlayFolder == null ? null : mOverlayFolder.getFile(); 231 } 232 233 /** Returns whether the partition contains the specified file. */ containsPath(@onNull String path)234 public boolean containsPath(@NonNull String path) { 235 return containsFile(new File(path)); 236 } 237 238 /** Returns whether the partition contains the specified file. */ containsFile(@onNull File file)239 public boolean containsFile(@NonNull File file) { 240 return FileUtils.contains(mFolder.getFile(), canonicalize(file)); 241 } 242 243 /** Returns whether the partition contains the specified file in its priv-app folder. */ containsPrivApp(@onNull File scanFile)244 public boolean containsPrivApp(@NonNull File scanFile) { 245 return mPrivAppFolder != null 246 && FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile)); 247 } 248 249 /** Returns whether the partition contains the specified file in its app folder. */ containsApp(@onNull File scanFile)250 public boolean containsApp(@NonNull File scanFile) { 251 return mAppFolder != null 252 && FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile)); 253 } 254 255 /** Returns whether the partition contains the specified file in its overlay folder. */ containsOverlay(@onNull File scanFile)256 public boolean containsOverlay(@NonNull File scanFile) { 257 return mOverlayFolder != null 258 && FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile)); 259 } 260 } 261 262 /** 263 * A class that defers the canonicalization of its underlying file. This must be done so 264 * processes do not attempt to canonicalize files in directories for which the process does not 265 * have the correct selinux policies. 266 */ 267 private static class DeferredCanonicalFile { 268 private boolean mIsCanonical = false; 269 270 @NonNull 271 private File mFile; 272 DeferredCanonicalFile(@onNull File dir)273 private DeferredCanonicalFile(@NonNull File dir) { 274 mFile = dir; 275 } 276 DeferredCanonicalFile(@onNull File dir, @NonNull String fileName)277 private DeferredCanonicalFile(@NonNull File dir, @NonNull String fileName) { 278 mFile = new File(dir, fileName); 279 } 280 281 @NonNull getFile()282 private File getFile() { 283 if (!mIsCanonical) { 284 mFile = canonicalize(mFile); 285 mIsCanonical = true; 286 } 287 return mFile; 288 } 289 } 290 } 291