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(flag = true, 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 SystemPartition(@onNull File folder, @PartitionType int type, boolean containsPrivApp, boolean containsOverlay)122 private SystemPartition(@NonNull File folder, @PartitionType int type, 123 boolean containsPrivApp, boolean containsOverlay) { 124 this.type = type; 125 this.mFolder = new DeferredCanonicalFile(folder); 126 this.mAppFolder = new DeferredCanonicalFile(folder, "app"); 127 this.mPrivAppFolder = containsPrivApp ? new DeferredCanonicalFile(folder, "priv-app") 128 : null; 129 this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay") 130 : null; 131 } 132 SystemPartition(@onNull SystemPartition original)133 public SystemPartition(@NonNull SystemPartition original) { 134 this.type = original.type; 135 this.mFolder = new DeferredCanonicalFile(original.mFolder.getFile()); 136 this.mAppFolder = original.mAppFolder; 137 this.mPrivAppFolder = original.mPrivAppFolder; 138 this.mOverlayFolder = original.mOverlayFolder; 139 } 140 141 /** 142 * Creates a partition containing the same folders as the original partition but with a 143 * different root folder. 144 */ SystemPartition(@onNull File rootFolder, @NonNull SystemPartition partition)145 public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) { 146 this(rootFolder, partition.type, partition.mPrivAppFolder != null, 147 partition.mOverlayFolder != null); 148 } 149 150 /** Returns the canonical folder of the partition. */ 151 @NonNull getFolder()152 public File getFolder() { 153 return mFolder.getFile(); 154 } 155 156 /** Returns the canonical app folder of the partition. */ 157 @Nullable getAppFolder()158 public File getAppFolder() { 159 return mAppFolder == null ? null : mAppFolder.getFile(); 160 } 161 162 /** Returns the canonical priv-app folder of the partition, if one exists. */ 163 @Nullable getPrivAppFolder()164 public File getPrivAppFolder() { 165 return mPrivAppFolder == null ? null : mPrivAppFolder.getFile(); 166 } 167 168 /** Returns the canonical overlay folder of the partition, if one exists. */ 169 @Nullable getOverlayFolder()170 public File getOverlayFolder() { 171 return mOverlayFolder == null ? null : mOverlayFolder.getFile(); 172 } 173 174 /** Returns whether the partition contains the specified file. */ containsPath(@onNull String path)175 public boolean containsPath(@NonNull String path) { 176 return containsFile(new File(path)); 177 } 178 179 /** Returns whether the partition contains the specified file. */ containsFile(@onNull File file)180 public boolean containsFile(@NonNull File file) { 181 return FileUtils.contains(mFolder.getFile(), canonicalize(file)); 182 } 183 184 /** Returns whether the partition contains the specified file in its priv-app folder. */ containsPrivApp(@onNull File scanFile)185 public boolean containsPrivApp(@NonNull File scanFile) { 186 return mPrivAppFolder != null 187 && FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile)); 188 } 189 190 /** Returns whether the partition contains the specified file in its app folder. */ containsApp(@onNull File scanFile)191 public boolean containsApp(@NonNull File scanFile) { 192 return mAppFolder != null 193 && FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile)); 194 } 195 196 /** Returns whether the partition contains the specified file in its overlay folder. */ containsOverlay(@onNull File scanFile)197 public boolean containsOverlay(@NonNull File scanFile) { 198 return mOverlayFolder != null 199 && FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile)); 200 } 201 } 202 203 /** 204 * A class that defers the canonicalization of its underlying file. This must be done so 205 * processes do not attempt to canonicalize files in directories for which the process does not 206 * have the correct selinux policies. 207 */ 208 private static class DeferredCanonicalFile { 209 private boolean mIsCanonical = false; 210 211 @NonNull 212 private File mFile; 213 DeferredCanonicalFile(@onNull File dir)214 private DeferredCanonicalFile(@NonNull File dir) { 215 mFile = dir; 216 } 217 DeferredCanonicalFile(@onNull File dir, @NonNull String fileName)218 private DeferredCanonicalFile(@NonNull File dir, @NonNull String fileName) { 219 mFile = new File(dir, fileName); 220 } 221 222 @NonNull getFile()223 private File getFile() { 224 if (!mIsCanonical) { 225 mFile = canonicalize(mFile); 226 mIsCanonical = true; 227 } 228 return mFile; 229 } 230 } 231 } 232