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