File: PackagePartitions.java

package info (click to toggle)
android-platform-frameworks-base 1%3A14~beta1-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 326,084 kB
  • sloc: java: 2,032,373; xml: 343,016; cpp: 304,181; python: 3,683; ansic: 2,090; sh: 1,871; makefile: 120; sed: 19
file content (290 lines) | stat: -rw-r--r-- 11,408 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.pm;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Build;
import android.os.Build.Partition;
import android.os.Environment;
import android.os.FileUtils;
import android.os.SystemProperties;

import com.android.internal.annotations.VisibleForTesting;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Function;

/**
 * Exposes {@link #SYSTEM_PARTITIONS} which represents the partitions in which application packages
 * can be installed. The partitions are ordered from most generic (lowest priority) to most specific
 * (greatest priority).
 *
 * @hide
 **/
public class PackagePartitions {
    public static final int PARTITION_SYSTEM = 0;
    public static final int PARTITION_VENDOR = 1;
    public static final int PARTITION_ODM = 2;
    public static final int PARTITION_OEM = 3;
    public static final int PARTITION_PRODUCT = 4;
    public static final int PARTITION_SYSTEM_EXT = 5;

    @IntDef(prefix = { "PARTITION_" }, value = {
        PARTITION_SYSTEM,
        PARTITION_VENDOR,
        PARTITION_ODM,
        PARTITION_OEM,
        PARTITION_PRODUCT,
        PARTITION_SYSTEM_EXT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PartitionType {}

    /**
     * The list of all system partitions that may contain packages in ascending order of
     * specificity (the more generic, the earlier in the list a partition appears).
     */
    private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS =
            new ArrayList<>(Arrays.asList(
                    new SystemPartition(Environment.getRootDirectory(),
                            PARTITION_SYSTEM, Partition.PARTITION_NAME_SYSTEM,
                            true /* containsPrivApp */, false /* containsOverlay */),
                    new SystemPartition(Environment.getVendorDirectory(),
                            PARTITION_VENDOR, Partition.PARTITION_NAME_VENDOR,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getOdmDirectory(),
                            PARTITION_ODM, Partition.PARTITION_NAME_ODM,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getOemDirectory(),
                            PARTITION_OEM, Partition.PARTITION_NAME_OEM,
                            false /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getProductDirectory(),
                            PARTITION_PRODUCT, Partition.PARTITION_NAME_PRODUCT,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getSystemExtDirectory(),
                            PARTITION_SYSTEM_EXT, Partition.PARTITION_NAME_SYSTEM_EXT,
                            true /* containsPrivApp */, true /* containsOverlay */)));

    /**
     * A string to represent the fingerprint of this build and all package partitions. Using it to
     * determine whether the system update has occurred. Different from {@link Build#FINGERPRINT},
     * this string is digested from the fingerprints of the build and all package partitions to
     * help detect the partition update.
     */
    public static final String FINGERPRINT = getFingerprint();

    /**
     * Returns a list in which the elements are products of the specified function applied to the
     * list of {@link #SYSTEM_PARTITIONS} in increasing specificity order.
     */
    public static <T> ArrayList<T> getOrderedPartitions(
            @NonNull Function<SystemPartition, T> producer) {
        final ArrayList<T> out = new ArrayList<>();
        for (int i = 0, n = SYSTEM_PARTITIONS.size(); i < n; i++) {
            final T v = producer.apply(SYSTEM_PARTITIONS.get(i));
            if (v != null)  {
                out.add(v);
            }
        }
        return out;
    }

    private static File canonicalize(File path) {
        try {
            return path.getCanonicalFile();
        } catch (IOException e) {
            return path;
        }
    }

    /**
     * Returns a fingerprint string for this build and all package partitions. The string is
     * digested from the fingerprints of the build and all package partitions.
     *
     * @return A string to represent the fingerprint of this build and all package partitions.
     */
    @NonNull
    private static String getFingerprint() {
        final String[] digestProperties = new String[SYSTEM_PARTITIONS.size() + 1];
        for (int i = 0; i < SYSTEM_PARTITIONS.size(); i++) {
            final String partitionName = SYSTEM_PARTITIONS.get(i).getName();
            digestProperties[i] = "ro." + partitionName + ".build.fingerprint";
        }
        digestProperties[SYSTEM_PARTITIONS.size()] = "ro.build.fingerprint"; // build fingerprint
        return SystemProperties.digestOf(digestProperties);
    }

    /** Represents a partition that contains application packages. */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public static class SystemPartition {
        @PartitionType
        public final int type;

        @NonNull
        private final String mName;

        @NonNull
        private final DeferredCanonicalFile mFolder;

        @Nullable
        private final DeferredCanonicalFile mAppFolder;

        @Nullable
        private final DeferredCanonicalFile mPrivAppFolder;

        @Nullable
        private final DeferredCanonicalFile mOverlayFolder;

        @NonNull
        private final File mNonConicalFolder;

        private SystemPartition(@NonNull File folder, @PartitionType int type, String name,
                boolean containsPrivApp, boolean containsOverlay) {
            this.type = type;
            this.mName = name;
            this.mFolder = new DeferredCanonicalFile(folder);
            this.mAppFolder = new DeferredCanonicalFile(folder, "app");
            this.mPrivAppFolder = containsPrivApp ? new DeferredCanonicalFile(folder, "priv-app")
                    : null;
            this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay")
                    : null;
            this.mNonConicalFolder = folder;
        }

        public SystemPartition(@NonNull SystemPartition original) {
            this.type = original.type;
            this.mName = original.mName;
            this.mFolder = new DeferredCanonicalFile(original.mFolder.getFile());
            this.mAppFolder = original.mAppFolder;
            this.mPrivAppFolder = original.mPrivAppFolder;
            this.mOverlayFolder = original.mOverlayFolder;
            this.mNonConicalFolder = original.mNonConicalFolder;
        }

        /**
         * Creates a partition containing the same folders as the original partition but with a
         * different root folder.
         */
        public SystemPartition(@NonNull File rootFolder, @NonNull SystemPartition partition) {
            this(rootFolder, partition.type, partition.mName, partition.mPrivAppFolder != null,
                    partition.mOverlayFolder != null);
        }

        /**
         * Returns the name identifying the partition.
         * @see Partition
         */
        @NonNull
        public String getName() {
            return mName;
        }

        /** Returns the canonical folder of the partition. */
        @NonNull
        public File getFolder() {
            return mFolder.getFile();
        }

        /** Returns the non-canonical folder of the partition. */
        @NonNull
        public File getNonConicalFolder() {
            return mNonConicalFolder;
        }

        /** Returns the canonical app folder of the partition. */
        @Nullable
        public File getAppFolder() {
            return mAppFolder == null ? null : mAppFolder.getFile();
        }

        /** Returns the canonical priv-app folder of the partition, if one exists. */
        @Nullable
        public File getPrivAppFolder() {
            return mPrivAppFolder == null ? null : mPrivAppFolder.getFile();
        }

        /** Returns the canonical overlay folder of the partition, if one exists. */
        @Nullable
        public File getOverlayFolder() {
            return mOverlayFolder == null ? null : mOverlayFolder.getFile();
        }

        /** Returns whether the partition contains the specified file. */
        public boolean containsPath(@NonNull String path) {
            return containsFile(new File(path));
        }

        /** Returns whether the partition contains the specified file. */
        public boolean containsFile(@NonNull File file) {
            return FileUtils.contains(mFolder.getFile(), canonicalize(file));
        }

        /** Returns whether the partition contains the specified file in its priv-app folder. */
        public boolean containsPrivApp(@NonNull File scanFile) {
            return mPrivAppFolder != null
                    && FileUtils.contains(mPrivAppFolder.getFile(), canonicalize(scanFile));
        }

        /** Returns whether the partition contains the specified file in its app folder. */
        public boolean containsApp(@NonNull File scanFile) {
            return mAppFolder != null
                    && FileUtils.contains(mAppFolder.getFile(), canonicalize(scanFile));
        }

        /** Returns whether the partition contains the specified file in its overlay folder. */
        public boolean containsOverlay(@NonNull File scanFile) {
            return mOverlayFolder != null
                    && FileUtils.contains(mOverlayFolder.getFile(), canonicalize(scanFile));
        }
    }

    /**
     * A class that defers the canonicalization of its underlying file. This must be done so
     * processes do not attempt to canonicalize files in directories for which the process does not
     * have the correct selinux policies.
     */
    private static class DeferredCanonicalFile {
        private boolean mIsCanonical = false;

        @NonNull
        private File mFile;

        private DeferredCanonicalFile(@NonNull File dir) {
            mFile = dir;
        }

        private DeferredCanonicalFile(@NonNull File dir, @NonNull String fileName) {
            mFile = new File(dir, fileName);
        }

        @NonNull
        private File getFile() {
            if (!mIsCanonical) {
                mFile = canonicalize(mFile);
                mIsCanonical = true;
            }
            return mFile;
        }
    }
}