• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.server.pm;
18 
19 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.apex.ApexInfo;
25 import android.apex.ApexInfoList;
26 import android.apex.ApexSessionInfo;
27 import android.apex.ApexSessionParams;
28 import android.apex.CompressedApexInfoList;
29 import android.apex.IApexService;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.PackageInfo;
32 import android.content.pm.PackageInstaller;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageParser.PackageParserException;
35 import android.content.pm.PackageParser.SigningDetails;
36 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
37 import android.content.pm.parsing.ParsingPackageUtils;
38 import android.os.Binder;
39 import android.os.Environment;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.os.Trace;
43 import android.sysprop.ApexProperties;
44 import android.util.ArrayMap;
45 import android.util.ArraySet;
46 import android.util.Singleton;
47 import android.util.Slog;
48 import android.util.SparseArray;
49 import android.util.apk.ApkSignatureVerifier;
50 
51 import com.android.internal.annotations.GuardedBy;
52 import com.android.internal.annotations.VisibleForTesting;
53 import com.android.internal.util.IndentingPrintWriter;
54 import com.android.internal.util.Preconditions;
55 import com.android.server.pm.parsing.PackageParser2;
56 import com.android.server.pm.parsing.pkg.AndroidPackage;
57 import com.android.server.pm.parsing.pkg.ParsedPackage;
58 import com.android.server.utils.TimingsTraceAndSlog;
59 
60 import com.google.android.collect.Lists;
61 
62 import java.io.File;
63 import java.io.PrintWriter;
64 import java.lang.annotation.Retention;
65 import java.lang.annotation.RetentionPolicy;
66 import java.util.ArrayList;
67 import java.util.Collections;
68 import java.util.HashSet;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.Objects;
72 import java.util.Set;
73 import java.util.concurrent.ExecutorService;
74 
75 /**
76  * ApexManager class handles communications with the apex service to perform operation and queries,
77  * as well as providing caching to avoid unnecessary calls to the service.
78  */
79 public abstract class ApexManager {
80 
81     private static final String TAG = "ApexManager";
82 
83     public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
84     static final int MATCH_FACTORY_PACKAGE = 1 << 1;
85 
86     private static final String VNDK_APEX_MODULE_NAME_PREFIX = "com.android.vndk.";
87 
88     private static final Singleton<ApexManager> sApexManagerSingleton =
89             new Singleton<ApexManager>() {
90                 @Override
91                 protected ApexManager create() {
92                     if (ApexProperties.updatable().orElse(false)) {
93                         return new ApexManagerImpl();
94                     } else {
95                         return new ApexManagerFlattenedApex();
96                     }
97                 }
98             };
99 
100     /**
101      * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
102      * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
103      * evaluates to {@code true}.
104      * @hide
105      */
getInstance()106     public static ApexManager getInstance() {
107         return sApexManagerSingleton.get();
108     }
109 
110     /**
111      * Minimal information about APEX mount points and the original APEX package they refer to.
112      * @hide
113      */
114     public static class ActiveApexInfo {
115         @Nullable public final String apexModuleName;
116         public final File apexDirectory;
117         public final File preInstalledApexPath;
118 
ActiveApexInfo(File apexDirectory, File preInstalledApexPath)119         private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) {
120             this(null, apexDirectory, preInstalledApexPath);
121         }
122 
ActiveApexInfo(@ullable String apexModuleName, File apexDirectory, File preInstalledApexPath)123         private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
124                 File preInstalledApexPath) {
125             this.apexModuleName = apexModuleName;
126             this.apexDirectory = apexDirectory;
127             this.preInstalledApexPath = preInstalledApexPath;
128         }
129 
ActiveApexInfo(ApexInfo apexInfo)130         private ActiveApexInfo(ApexInfo apexInfo) {
131             this(
132                     apexInfo.moduleName,
133                     new File(Environment.getApexDirectory() + File.separator
134                             + apexInfo.moduleName),
135                     new File(apexInfo.preinstalledModulePath));
136         }
137     }
138 
139     /**
140      * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
141      *
142      * @hide
143      */
getActiveApexInfos()144     public abstract List<ActiveApexInfo> getActiveApexInfos();
145 
146     /**
147      * Called by package manager service to scan apex package files when device boots up.
148      *
149      * @param packageParser The package parser to support apex package parsing and caching parsed
150      *                      results.
151      * @param executorService An executor to support parallel package parsing.
152      */
scanApexPackagesTraced(@onNull PackageParser2 packageParser, @NonNull ExecutorService executorService)153     abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
154             @NonNull ExecutorService executorService);
155 
156     /**
157      * Retrieves information about an APEX package.
158      *
159      * @param packageName the package name to look for. Note that this is the package name reported
160      *                    in the APK container manifest (i.e. AndroidManifest.xml), which might
161      *                    differ from the one reported in the APEX manifest (i.e.
162      *                    apex_manifest.json).
163      * @param flags the type of package to return. This may match to active packages
164      *              and factory (pre-installed) packages.
165      * @return a PackageInfo object with the information about the package, or null if the package
166      *         is not found.
167      */
168     @Nullable
getPackageInfo(String packageName, @PackageInfoFlags int flags)169     public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
170 
171     /**
172      * Retrieves information about all active APEX packages.
173      *
174      * @return a List of PackageInfo object, each one containing information about a different
175      *         active package.
176      */
getActivePackages()177     abstract List<PackageInfo> getActivePackages();
178 
179     /**
180      * Retrieves information about all active pre-installed APEX packages.
181      *
182      * @return a List of PackageInfo object, each one containing information about a different
183      *         active pre-installed package.
184      */
getFactoryPackages()185     abstract List<PackageInfo> getFactoryPackages();
186 
187     /**
188      * Retrieves information about all inactive APEX packages.
189      *
190      * @return a List of PackageInfo object, each one containing information about a different
191      *         inactive package.
192      */
getInactivePackages()193     abstract List<PackageInfo> getInactivePackages();
194 
195     /**
196      * Checks if {@code packageName} is an apex package.
197      *
198      * @param packageName package to check.
199      * @return {@code true} if {@code packageName} is an apex package.
200      */
isApexPackage(String packageName)201     abstract boolean isApexPackage(String packageName);
202 
203     /**
204      * Whether the APEX package is pre-installed or not.
205      *
206      * @param packageInfo the package to check
207      * @return {@code true} if this package is pre-installed, {@code false} otherwise.
208      */
isFactory(@onNull PackageInfo packageInfo)209     public static boolean isFactory(@NonNull PackageInfo packageInfo) {
210         return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
211     }
212 
213     /**
214      * Returns the active apex package's name that contains the (apk) package.
215      *
216      * @param containedPackageName The (apk) package that might be in a apex
217      * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside
218      *         any apex.
219      */
220     @Nullable
getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)221     public abstract String getActiveApexPackageNameContainingPackage(
222             @NonNull String containedPackageName);
223 
224     /**
225      * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
226      * track the different states of a session.
227      *
228      * @param sessionId the identifier of the session.
229      * @return an ApexSessionInfo object, or null if the session is not known.
230      */
231     @Nullable
getStagedSessionInfo(int sessionId)232     abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
233 
234     /**
235      * Returns array of all staged sessions known to apexd.
236      */
237     @NonNull
getSessions()238     abstract SparseArray<ApexSessionInfo> getSessions();
239 
240     /**
241      * Submit a staged session to apex service. This causes the apex service to perform some initial
242      * verification and accept or reject the session. Submitting a session successfully is not
243      * enough for it to be activated at the next boot, the caller needs to call
244      * {@link #markStagedSessionReady(int)}.
245      *
246      * @throws PackageManagerException if call to apexd fails
247      */
submitStagedSession(ApexSessionParams params)248     abstract ApexInfoList submitStagedSession(ApexSessionParams params)
249             throws PackageManagerException;
250 
251     /**
252      * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
253      * applied at next reboot.
254      *
255      * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
256      * @throws PackageManagerException if call to apexd fails
257      */
markStagedSessionReady(int sessionId)258     abstract void markStagedSessionReady(int sessionId) throws PackageManagerException;
259 
260     /**
261      * Marks a staged session as successful.
262      *
263      * <p>Only activated session can be marked as successful.
264      *
265      * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
266      *                  successful.
267      */
markStagedSessionSuccessful(int sessionId)268     abstract void markStagedSessionSuccessful(int sessionId);
269 
270     /**
271      * Whether the current device supports the management of APEX packages.
272      *
273      * @return true if APEX packages can be managed on this device, false otherwise.
274      */
isApexSupported()275     abstract boolean isApexSupported();
276 
277     /**
278      * Abandons the (only) active session previously submitted.
279      *
280      * @return {@code true} upon success, {@code false} if any remote exception occurs
281      */
revertActiveSessions()282     abstract boolean revertActiveSessions();
283 
284     /**
285      * Abandons the staged session with the given sessionId. Client should handle {@code false}
286      * return value carefully as failure here can leave device in inconsistent state.
287      *
288      * @return {@code true} upon success, {@code false} if any exception occurs
289      */
abortStagedSession(int sessionId)290     abstract boolean abortStagedSession(int sessionId);
291 
292     /**
293      * Uninstalls given {@code apexPackage}.
294      *
295      * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
296      *
297      * @param apexPackagePath package to uninstall.
298      * @return {@code true} upon successful uninstall, {@code false} otherwise.
299      */
uninstallApex(String apexPackagePath)300     abstract boolean uninstallApex(String apexPackagePath);
301 
302     /**
303      * Registers an APK package as an embedded apk of apex.
304      */
registerApkInApex(AndroidPackage pkg)305     abstract void registerApkInApex(AndroidPackage pkg);
306 
307     /**
308      * Reports error raised during installation of apk-in-apex.
309      *
310      * @param scanDirPath the directory of the apex inside which apk-in-apex resides.
311      * @param errorMsg the actual error that occurred when scanning the path
312      */
reportErrorWithApkInApex(String scanDirPath, String errorMsg)313     abstract void reportErrorWithApkInApex(String scanDirPath, String errorMsg);
314 
315     /**
316      * Returns null if there were no errors when installing apk-in-apex inside
317      * {@param apexPackageName}, otherwise returns the error as string
318      *
319      * @param apexPackageName Package name of the apk container of apex
320      */
321     @Nullable
getApkInApexInstallError(String apexPackageName)322     abstract String getApkInApexInstallError(String apexPackageName);
323 
324     /**
325      * Returns list of {@code packageName} of apks inside the given apex.
326      * @param apexPackageName Package name of the apk container of apex
327      */
getApksInApex(String apexPackageName)328     abstract List<String> getApksInApex(String apexPackageName);
329 
330     /**
331      * Returns the apex module name for the given package name, if the package is an APEX. Otherwise
332      * returns {@code null}.
333      */
334     @Nullable
getApexModuleNameForPackageName(String apexPackageName)335     public abstract String getApexModuleNameForPackageName(String apexPackageName);
336 
337     /**
338      * Copies the CE apex data directory for the given {@code userId} to a backup location, for use
339      * in case of rollback.
340      *
341      * @return boolean true if the snapshot was successful
342      */
snapshotCeData(int userId, int rollbackId, String apexPackageName)343     public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName);
344 
345     /**
346      * Restores the snapshot of the CE apex data directory for the given {@code userId}.
347      * Note the snapshot will be deleted after restoration succeeded.
348      *
349      * @return boolean true if the restore was successful
350      */
restoreCeData(int userId, int rollbackId, String apexPackageName)351     public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName);
352 
353     /**
354      * Deletes snapshots of the device encrypted apex data directories for the given
355      * {@code rollbackId}.
356      *
357      * @return boolean true if the delete was successful
358      */
destroyDeSnapshots(int rollbackId)359     public abstract boolean destroyDeSnapshots(int rollbackId);
360 
361     /**
362      *  Deletes snapshots of the credential encrypted apex data directories for the specified user,
363      *  for the given rollback id as long as the user is credential unlocked.
364      *
365      * @return boolean true if the delete was successful
366      */
destroyCeSnapshots(int userId, int rollbackId)367     public abstract boolean destroyCeSnapshots(int userId, int rollbackId);
368 
369     /**
370      * Deletes snapshots of the credential encrypted apex data directories for the specified user,
371      * where the rollback id is not included in {@code retainRollbackIds} as long as the user is
372      * credential unlocked.
373      *
374      * @return boolean true if the delete was successful
375      */
destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)376     public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
377 
378     /**
379      * Inform apexd that the boot has completed.
380      */
markBootCompleted()381     public abstract void markBootCompleted();
382 
383     /**
384      * Estimate how much storage space is needed on /data/ for decompressing apexes
385      * @param infoList List of apexes that are compressed in target build.
386      * @return Size, in bytes, the amount of space needed on /data/
387      */
calculateSizeForCompressedApex(CompressedApexInfoList infoList)388     public abstract long calculateSizeForCompressedApex(CompressedApexInfoList infoList)
389             throws RemoteException;
390 
391     /**
392      * Reserve space on /data so that apexes can be decompressed after OTA
393      * @param infoList List of apexes that are compressed in target build.
394      */
reserveSpaceForCompressedApex(CompressedApexInfoList infoList)395     public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList)
396             throws RemoteException;
397 
398     /**
399      * Performs a non-staged install of the given {@code apexFile}.
400      */
installPackage(File apexFile, PackageParser2 packageParser)401     abstract void installPackage(File apexFile, PackageParser2 packageParser)
402             throws PackageManagerException;
403 
404     /**
405      * Dumps various state information to the provided {@link PrintWriter} object.
406      *
407      * @param pw the {@link PrintWriter} object to send information to.
408      * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
409      *                    information about that specific package will be dumped.
410      */
dump(PrintWriter pw, @Nullable String packageName)411     abstract void dump(PrintWriter pw, @Nullable String packageName);
412 
413     @IntDef(
414             flag = true,
415             prefix = { "MATCH_"},
416             value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
417     @Retention(RetentionPolicy.SOURCE)
418     @interface PackageInfoFlags{}
419 
420     /**
421      * An implementation of {@link ApexManager} that should be used in case device supports updating
422      * APEX packages.
423      */
424     @VisibleForTesting
425     protected static class ApexManagerImpl extends ApexManager {
426         private final Object mLock = new Object();
427 
428         @GuardedBy("mLock")
429         private Set<ActiveApexInfo> mActiveApexInfosCache;
430 
431         /**
432          * Contains the list of {@code packageName}s of apks-in-apex for given
433          * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
434          * difference between {@code packageName} and {@code apexModuleName}.
435          */
436         @GuardedBy("mLock")
437         private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
438 
439         /**
440          * Contains the list of {@code Exception}s that were raised when installing apk-in-apex
441          * inside {@code apexModuleName}.
442          */
443         @GuardedBy("mLock")
444         private Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
445 
446         @GuardedBy("mLock")
447         private List<PackageInfo> mAllPackagesCache;
448 
449         /**
450          * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
451          * apk container has a reference name, called {@code packageName}, which is found inside the
452          * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference
453          * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file.
454          *
455          * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of
456          * the apk container to {@code apexModuleName} of the apex-payload inside.
457          */
458         @GuardedBy("mLock")
459         private ArrayMap<String, String> mPackageNameToApexModuleName;
460 
461         /**
462          * Whether an APEX package is active or not.
463          *
464          * @param packageInfo the package to check
465          * @return {@code true} if this package is active, {@code false} otherwise.
466          */
isActive(PackageInfo packageInfo)467         private static boolean isActive(PackageInfo packageInfo) {
468             return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
469         }
470 
471         /**
472          * Retrieve the service from ServiceManager. If the service is not running, it will be
473          * started, and this function will block until it is ready.
474          */
475         @VisibleForTesting
waitForApexService()476         protected IApexService waitForApexService() {
477             // Since apexd is a trusted platform component, synchronized calls are allowable
478             return IApexService.Stub.asInterface(
479                     Binder.allowBlocking(ServiceManager.waitForService("apexservice")));
480         }
481 
482         @Override
getActiveApexInfos()483         public List<ActiveApexInfo> getActiveApexInfos() {
484             final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
485                     Trace.TRACE_TAG_APEX_MANAGER);
486             synchronized (mLock) {
487                 if (mActiveApexInfosCache == null) {
488                     t.traceBegin("getActiveApexInfos_noCache");
489                     try {
490                         mActiveApexInfosCache = new ArraySet<>();
491                         final ApexInfo[] activePackages = waitForApexService().getActivePackages();
492                         for (int i = 0; i < activePackages.length; i++) {
493                             ApexInfo apexInfo = activePackages[i];
494                             mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
495                         }
496                     } catch (RemoteException e) {
497                         Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
498                     }
499                     t.traceEnd();
500                 }
501                 if (mActiveApexInfosCache != null) {
502                     return new ArrayList<>(mActiveApexInfosCache);
503                 } else {
504                     return Collections.emptyList();
505                 }
506             }
507         }
508 
509         @Override
scanApexPackagesTraced(@onNull PackageParser2 packageParser, @NonNull ExecutorService executorService)510         void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
511                 @NonNull ExecutorService executorService) {
512             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackagesTraced");
513             try {
514                 synchronized (mLock) {
515                     scanApexPackagesInternalLocked(packageParser, executorService);
516                 }
517             } finally {
518                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
519             }
520         }
521 
522         @GuardedBy("mLock")
scanApexPackagesInternalLocked(PackageParser2 packageParser, ExecutorService executorService)523         private void scanApexPackagesInternalLocked(PackageParser2 packageParser,
524                 ExecutorService executorService) {
525             final ApexInfo[] allPkgs;
526             try {
527                 mAllPackagesCache = new ArrayList<>();
528                 mPackageNameToApexModuleName = new ArrayMap<>();
529                 allPkgs = waitForApexService().getAllPackages();
530             } catch (RemoteException re) {
531                 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
532                 throw new RuntimeException(re);
533             }
534             if (allPkgs.length == 0) {
535                 return;
536             }
537             final int flags = PackageManager.GET_META_DATA
538                     | PackageManager.GET_SIGNING_CERTIFICATES
539                     | PackageManager.GET_SIGNATURES;
540             ArrayMap<File, ApexInfo> parsingApexInfo = new ArrayMap<>();
541             ParallelPackageParser parallelPackageParser =
542                     new ParallelPackageParser(packageParser, executorService);
543 
544             for (ApexInfo ai : allPkgs) {
545                 File apexFile = new File(ai.modulePath);
546                 parallelPackageParser.submit(apexFile,
547                         ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
548                 parsingApexInfo.put(apexFile, ai);
549             }
550 
551             HashSet<String> activePackagesSet = new HashSet<>();
552             HashSet<String> factoryPackagesSet = new HashSet<>();
553             // Process results one by one
554             for (int i = 0; i < parsingApexInfo.size(); i++) {
555                 ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
556                 Throwable throwable = parseResult.throwable;
557                 ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
558 
559                 if (throwable == null) {
560                     final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate(
561                             parseResult.parsedPackage, ai, flags);
562                     if (packageInfo == null) {
563                         throw new IllegalStateException("Unable to generate package info: "
564                                 + ai.modulePath);
565                     }
566                     mAllPackagesCache.add(packageInfo);
567                     mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
568                     if (ai.isActive) {
569                         if (activePackagesSet.contains(packageInfo.packageName)) {
570                             throw new IllegalStateException(
571                                     "Two active packages have the same name: "
572                                             + packageInfo.packageName);
573                         }
574                         activePackagesSet.add(packageInfo.packageName);
575                     }
576                     if (ai.isFactory) {
577                         // Don't throw when the duplicating APEX is VNDK APEX
578                         if (factoryPackagesSet.contains(packageInfo.packageName)
579                                 && !ai.moduleName.startsWith(VNDK_APEX_MODULE_NAME_PREFIX)) {
580                             throw new IllegalStateException(
581                                     "Two factory packages have the same name: "
582                                             + packageInfo.packageName);
583                         }
584                         factoryPackagesSet.add(packageInfo.packageName);
585                     }
586                 } else if (throwable instanceof PackageParserException) {
587                     final PackageParserException e = (PackageParserException) throwable;
588                     // Skip parsing non-coreApp apex file if system is in minimal boot state.
589                     if (e.error == PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED) {
590                         Slog.w(TAG, "Scan apex failed, not a coreApp:" + ai.modulePath);
591                         continue;
592                     }
593                     throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable);
594                 } else {
595                     throw new IllegalStateException("Unexpected exception occurred while parsing "
596                             + ai.modulePath, throwable);
597                 }
598             }
599         }
600 
601         @Override
602         @Nullable
getPackageInfo(String packageName, @PackageInfoFlags int flags)603         public PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
604             synchronized (mLock) {
605                 Preconditions.checkState(mAllPackagesCache != null,
606                         "APEX packages have not been scanned");
607                 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
608                 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
609                 for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
610                     final PackageInfo packageInfo = mAllPackagesCache.get(i);
611                     if (!packageInfo.packageName.equals(packageName)) {
612                         continue;
613                     }
614                     if ((matchActive && isActive(packageInfo))
615                             || (matchFactory && isFactory(packageInfo))) {
616                         return packageInfo;
617                     }
618                 }
619                 return null;
620             }
621         }
622 
623         @Override
getActivePackages()624         List<PackageInfo> getActivePackages() {
625             synchronized (mLock) {
626                 Preconditions.checkState(mAllPackagesCache != null,
627                         "APEX packages have not been scanned");
628                 final List<PackageInfo> activePackages = new ArrayList<>();
629                 for (int i = 0; i < mAllPackagesCache.size(); i++) {
630                     final PackageInfo packageInfo = mAllPackagesCache.get(i);
631                     if (isActive(packageInfo)) {
632                         activePackages.add(packageInfo);
633                     }
634                 }
635                 return activePackages;
636             }
637         }
638 
639         @Override
getFactoryPackages()640         List<PackageInfo> getFactoryPackages() {
641             synchronized (mLock) {
642                 Preconditions.checkState(mAllPackagesCache != null,
643                         "APEX packages have not been scanned");
644                 final List<PackageInfo> factoryPackages = new ArrayList<>();
645                 for (int i = 0; i < mAllPackagesCache.size(); i++) {
646                     final PackageInfo packageInfo = mAllPackagesCache.get(i);
647                     if (isFactory(packageInfo)) {
648                         factoryPackages.add(packageInfo);
649                     }
650                 }
651                 return factoryPackages;
652             }
653         }
654 
655         @Override
getInactivePackages()656         List<PackageInfo> getInactivePackages() {
657             synchronized (mLock) {
658                 Preconditions.checkState(mAllPackagesCache != null,
659                         "APEX packages have not been scanned");
660                 final List<PackageInfo> inactivePackages = new ArrayList<>();
661                 for (int i = 0; i < mAllPackagesCache.size(); i++) {
662                     final PackageInfo packageInfo = mAllPackagesCache.get(i);
663                     if (!isActive(packageInfo)) {
664                         inactivePackages.add(packageInfo);
665                     }
666                 }
667                 return inactivePackages;
668             }
669         }
670 
671         @Override
isApexPackage(String packageName)672         boolean isApexPackage(String packageName) {
673             if (!isApexSupported()) return false;
674             synchronized (mLock) {
675                 Preconditions.checkState(mAllPackagesCache != null,
676                         "APEX packages have not been scanned");
677                 for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
678                     final PackageInfo packageInfo = mAllPackagesCache.get(i);
679                     if (packageInfo.packageName.equals(packageName)) {
680                         return true;
681                     }
682                 }
683             }
684             return false;
685         }
686 
687         @Override
688         @Nullable
getActiveApexPackageNameContainingPackage(String containedPackageName)689         public String getActiveApexPackageNameContainingPackage(String containedPackageName) {
690             Objects.requireNonNull(containedPackageName);
691             synchronized (mLock) {
692                 Preconditions.checkState(mPackageNameToApexModuleName != null,
693                         "APEX packages have not been scanned");
694                 int numApksInApex = mApksInApex.size();
695                 for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) {
696                     if (mApksInApex.valueAt(apkInApexNum).contains(containedPackageName)) {
697                         String apexModuleName = mApksInApex.keyAt(apkInApexNum);
698 
699                         int numApexPkgs = mPackageNameToApexModuleName.size();
700                         for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) {
701                             if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals(
702                                     apexModuleName)) {
703                                 return mPackageNameToApexModuleName.keyAt(apexPkgNum);
704                             }
705                         }
706                     }
707                 }
708             }
709 
710             return null;
711         }
712 
713         @Override
getStagedSessionInfo(int sessionId)714         @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
715             try {
716                 ApexSessionInfo apexSessionInfo =
717                         waitForApexService().getStagedSessionInfo(sessionId);
718                 if (apexSessionInfo.isUnknown) {
719                     return null;
720                 }
721                 return apexSessionInfo;
722             } catch (RemoteException re) {
723                 Slog.e(TAG, "Unable to contact apexservice", re);
724                 throw new RuntimeException(re);
725             }
726         }
727 
728         @Override
getSessions()729         SparseArray<ApexSessionInfo> getSessions() {
730             try {
731                 final ApexSessionInfo[] sessions = waitForApexService().getSessions();
732                 final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length);
733                 for (int i = 0; i < sessions.length; i++) {
734                     result.put(sessions[i].sessionId, sessions[i]);
735                 }
736                 return result;
737             } catch (RemoteException re) {
738                 Slog.e(TAG, "Unable to contact apexservice", re);
739                 throw new RuntimeException(re);
740             }
741         }
742 
743         @Override
submitStagedSession(ApexSessionParams params)744         ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
745             try {
746                 final ApexInfoList apexInfoList = new ApexInfoList();
747                 waitForApexService().submitStagedSession(params, apexInfoList);
748                 return apexInfoList;
749             } catch (RemoteException re) {
750                 Slog.e(TAG, "Unable to contact apexservice", re);
751                 throw new RuntimeException(re);
752             } catch (Exception e) {
753                 throw new PackageManagerException(
754                         PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
755                         "apexd verification failed : " + e.getMessage());
756             }
757         }
758 
759         @Override
markStagedSessionReady(int sessionId)760         void markStagedSessionReady(int sessionId) throws PackageManagerException {
761             try {
762                 waitForApexService().markStagedSessionReady(sessionId);
763             } catch (RemoteException re) {
764                 Slog.e(TAG, "Unable to contact apexservice", re);
765                 throw new RuntimeException(re);
766             } catch (Exception e) {
767                 throw new PackageManagerException(
768                         PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
769                         "Failed to mark apexd session as ready : " + e.getMessage());
770             }
771         }
772 
773         @Override
markStagedSessionSuccessful(int sessionId)774         void markStagedSessionSuccessful(int sessionId) {
775             try {
776                 waitForApexService().markStagedSessionSuccessful(sessionId);
777             } catch (RemoteException re) {
778                 Slog.e(TAG, "Unable to contact apexservice", re);
779                 throw new RuntimeException(re);
780             } catch (Exception e) {
781                 // It is fine to just log an exception in this case. APEXd will be able to recover
782                 // in case markStagedSessionSuccessful fails.
783                 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
784             }
785         }
786 
787         @Override
isApexSupported()788         boolean isApexSupported() {
789             return true;
790         }
791 
792         @Override
revertActiveSessions()793         boolean revertActiveSessions() {
794             try {
795                 waitForApexService().revertActiveSessions();
796                 return true;
797             } catch (RemoteException re) {
798                 Slog.e(TAG, "Unable to contact apexservice", re);
799                 return false;
800             } catch (Exception e) {
801                 Slog.e(TAG, e.getMessage(), e);
802                 return false;
803             }
804         }
805 
806         @Override
abortStagedSession(int sessionId)807         boolean abortStagedSession(int sessionId) {
808             try {
809                 waitForApexService().abortStagedSession(sessionId);
810                 return true;
811             } catch (Exception e) {
812                 Slog.e(TAG, e.getMessage(), e);
813                 return false;
814             }
815         }
816 
817         @Override
uninstallApex(String apexPackagePath)818         boolean uninstallApex(String apexPackagePath) {
819             try {
820                 waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
821                 return true;
822             } catch (Exception e) {
823                 return false;
824             }
825         }
826 
827         @Override
registerApkInApex(AndroidPackage pkg)828         void registerApkInApex(AndroidPackage pkg) {
829             synchronized (mLock) {
830                 for (ActiveApexInfo aai : mActiveApexInfosCache) {
831                     if (pkg.getBaseApkPath().startsWith(
832                             aai.apexDirectory.getAbsolutePath() + File.separator)) {
833                         List<String> apks = mApksInApex.get(aai.apexModuleName);
834                         if (apks == null) {
835                             apks = Lists.newArrayList();
836                             mApksInApex.put(aai.apexModuleName, apks);
837                         }
838                         Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of "
839                                 + aai.apexModuleName);
840                         apks.add(pkg.getPackageName());
841                     }
842                 }
843             }
844         }
845 
846         @Override
reportErrorWithApkInApex(String scanDirPath, String errorMsg)847         void reportErrorWithApkInApex(String scanDirPath, String errorMsg) {
848             synchronized (mLock) {
849                 for (ActiveApexInfo aai : mActiveApexInfosCache) {
850                     if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) {
851                         mErrorWithApkInApex.put(aai.apexModuleName, errorMsg);
852                     }
853                 }
854             }
855         }
856 
857         @Override
858         @Nullable
getApkInApexInstallError(String apexPackageName)859         String getApkInApexInstallError(String apexPackageName) {
860             synchronized (mLock) {
861                 Preconditions.checkState(mPackageNameToApexModuleName != null,
862                         "APEX packages have not been scanned");
863                 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
864                 if (moduleName == null) {
865                     return null;
866                 }
867                 return mErrorWithApkInApex.get(moduleName);
868             }
869         }
870 
871         @Override
getApksInApex(String apexPackageName)872         List<String> getApksInApex(String apexPackageName) {
873             synchronized (mLock) {
874                 Preconditions.checkState(mPackageNameToApexModuleName != null,
875                         "APEX packages have not been scanned");
876                 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
877                 if (moduleName == null) {
878                     return Collections.emptyList();
879                 }
880                 return mApksInApex.getOrDefault(moduleName, Collections.emptyList());
881             }
882         }
883 
884         @Override
885         @Nullable
getApexModuleNameForPackageName(String apexPackageName)886         public String getApexModuleNameForPackageName(String apexPackageName) {
887             synchronized (mLock) {
888                 Preconditions.checkState(mPackageNameToApexModuleName != null,
889                         "APEX packages have not been scanned");
890                 return mPackageNameToApexModuleName.get(apexPackageName);
891             }
892         }
893 
894         @Override
snapshotCeData(int userId, int rollbackId, String apexPackageName)895         public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
896             String apexModuleName;
897             synchronized (mLock) {
898                 Preconditions.checkState(mPackageNameToApexModuleName != null,
899                         "APEX packages have not been scanned");
900                 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
901             }
902             if (apexModuleName == null) {
903                 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
904                 return false;
905             }
906             try {
907                 waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
908                 return true;
909             } catch (Exception e) {
910                 Slog.e(TAG, e.getMessage(), e);
911                 return false;
912             }
913         }
914 
915         @Override
restoreCeData(int userId, int rollbackId, String apexPackageName)916         public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
917             String apexModuleName;
918             synchronized (mLock) {
919                 Preconditions.checkState(mPackageNameToApexModuleName != null,
920                         "APEX packages have not been scanned");
921                 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
922             }
923             if (apexModuleName == null) {
924                 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
925                 return false;
926             }
927             try {
928                 waitForApexService().restoreCeData(userId, rollbackId, apexModuleName);
929                 return true;
930             } catch (Exception e) {
931                 Slog.e(TAG, e.getMessage(), e);
932                 return false;
933             }
934         }
935 
936         @Override
destroyDeSnapshots(int rollbackId)937         public boolean destroyDeSnapshots(int rollbackId) {
938             try {
939                 waitForApexService().destroyDeSnapshots(rollbackId);
940                 return true;
941             } catch (Exception e) {
942                 Slog.e(TAG, e.getMessage(), e);
943                 return false;
944             }
945         }
946 
947         @Override
destroyCeSnapshots(int userId, int rollbackId)948         public boolean destroyCeSnapshots(int userId, int rollbackId) {
949             try {
950                 waitForApexService().destroyCeSnapshots(userId, rollbackId);
951                 return true;
952             } catch (Exception e) {
953                 Slog.e(TAG, e.getMessage(), e);
954                 return false;
955             }
956         }
957 
958         @Override
destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)959         public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
960             try {
961                 waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
962                 return true;
963             } catch (Exception e) {
964                 Slog.e(TAG, e.getMessage(), e);
965                 return false;
966             }
967         }
968 
969         @Override
markBootCompleted()970         public void markBootCompleted() {
971             try {
972                 waitForApexService().markBootCompleted();
973             } catch (RemoteException re) {
974                 Slog.e(TAG, "Unable to contact apexservice", re);
975             }
976         }
977 
978         @Override
calculateSizeForCompressedApex(CompressedApexInfoList infoList)979         public long calculateSizeForCompressedApex(CompressedApexInfoList infoList)
980                 throws RemoteException {
981             return waitForApexService().calculateSizeForCompressedApex(infoList);
982         }
983 
984         @Override
reserveSpaceForCompressedApex(CompressedApexInfoList infoList)985         public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList)
986                 throws RemoteException {
987             waitForApexService().reserveSpaceForCompressedApex(infoList);
988         }
989 
getSigningDetails(PackageInfo pkg)990         private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException {
991             int minSignatureScheme =
992                     ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
993                             pkg.applicationInfo.targetSdkVersion);
994             try {
995                 return ApkSignatureVerifier.verify(pkg.applicationInfo.sourceDir,
996                         minSignatureScheme);
997             } catch (PackageParserException e) {
998                 throw PackageManagerException.from(e);
999             }
1000         }
1001 
checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)1002         private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)
1003                 throws PackageManagerException {
1004             final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg);
1005             final SigningDetails newSigningDetails = getSigningDetails(newApexPkg);
1006             if (!newSigningDetails.checkCapability(existingSigningDetails,
1007                       SigningDetails.CertCapabilities.INSTALLED_DATA)) {
1008                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
1009                           "APK container signature of " + newApexPkg.applicationInfo.sourceDir
1010                                    + " is not compatible with currently installed on device");
1011             }
1012         }
1013 
checkDowngrade(PackageInfo existingApexPkg, PackageInfo newApexPkg)1014         private void checkDowngrade(PackageInfo existingApexPkg, PackageInfo newApexPkg)
1015                 throws PackageManagerException {
1016             final long currentVersionCode = existingApexPkg.applicationInfo.longVersionCode;
1017             final long newVersionCode = newApexPkg.applicationInfo.longVersionCode;
1018             if (currentVersionCode > newVersionCode) {
1019                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE,
1020                           "Downgrade of APEX package " + newApexPkg.packageName
1021                                   + " is not allowed");
1022             }
1023         }
1024 
1025         @Override
installPackage(File apexFile, PackageParser2 packageParser)1026         void installPackage(File apexFile, PackageParser2 packageParser)
1027                 throws PackageManagerException {
1028             try {
1029                 final int flags = PackageManager.GET_META_DATA
1030                         | PackageManager.GET_SIGNING_CERTIFICATES
1031                         | PackageManager.GET_SIGNATURES;
1032                 final ParsedPackage parsedPackage = packageParser.parsePackage(
1033                         apexFile, flags, /* useCaches= */ false);
1034                 final PackageInfo newApexPkg = PackageInfoWithoutStateUtils.generate(parsedPackage,
1035                         /* apexInfo= */ null, flags);
1036                 if (newApexPkg == null) {
1037                     throw new PackageManagerException(PackageManager.INSTALL_FAILED_INVALID_APK,
1038                             "Failed to generate package info for " + apexFile.getAbsolutePath());
1039                 }
1040                 final PackageInfo existingApexPkg = getPackageInfo(newApexPkg.packageName,
1041                         MATCH_ACTIVE_PACKAGE);
1042                 if (existingApexPkg == null) {
1043                     Slog.w(TAG, "Attempting to install new APEX package " + newApexPkg.packageName);
1044                     throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
1045                             "It is forbidden to install new APEX packages");
1046                 }
1047                 checkApexSignature(existingApexPkg, newApexPkg);
1048                 checkDowngrade(existingApexPkg, newApexPkg);
1049                 ApexInfo apexInfo = waitForApexService().installAndActivatePackage(
1050                         apexFile.getAbsolutePath());
1051                 final ParsedPackage parsedPackage2 = packageParser.parsePackage(
1052                         new File(apexInfo.modulePath), flags, /* useCaches= */ false);
1053                 final PackageInfo finalApexPkg = PackageInfoWithoutStateUtils.generate(
1054                         parsedPackage2, apexInfo, flags);
1055                 // Installation was successful, time to update mAllPackagesCache
1056                 synchronized (mLock) {
1057                     if (isFactory(existingApexPkg)) {
1058                         existingApexPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
1059                         mAllPackagesCache.add(finalApexPkg);
1060                     } else {
1061                         for (int i = 0, size = mAllPackagesCache.size(); i < size; i++) {
1062                             if (mAllPackagesCache.get(i).equals(existingApexPkg)) {
1063                                 mAllPackagesCache.set(i, finalApexPkg);
1064                                 break;
1065                             }
1066                         }
1067                     }
1068                 }
1069             } catch (RemoteException e) {
1070                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
1071                         "apexservice not available");
1072             } catch (Exception e) {
1073                 // TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here?
1074                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
1075                         e.getMessage());
1076             }
1077         }
1078 
1079         /**
1080          * Dump information about the packages contained in a particular cache
1081          * @param packagesCache the cache to print information about.
1082          * @param packageName a {@link String} containing a package name, or {@code null}. If set,
1083          *                    only information about that specific package will be dumped.
1084          * @param ipw the {@link IndentingPrintWriter} object to send information to.
1085          */
dumpFromPackagesCache( List<PackageInfo> packagesCache, @Nullable String packageName, IndentingPrintWriter ipw)1086         void dumpFromPackagesCache(
1087                 List<PackageInfo> packagesCache,
1088                 @Nullable String packageName,
1089                 IndentingPrintWriter ipw) {
1090             ipw.println();
1091             ipw.increaseIndent();
1092             for (int i = 0, size = packagesCache.size(); i < size; i++) {
1093                 final PackageInfo pi = packagesCache.get(i);
1094                 if (packageName != null && !packageName.equals(pi.packageName)) {
1095                     continue;
1096                 }
1097                 ipw.println(pi.packageName);
1098                 ipw.increaseIndent();
1099                 ipw.println("Version: " + pi.versionCode);
1100                 ipw.println("Path: " + pi.applicationInfo.sourceDir);
1101                 ipw.println("IsActive: " + isActive(pi));
1102                 ipw.println("IsFactory: " + isFactory(pi));
1103                 ipw.decreaseIndent();
1104             }
1105             ipw.decreaseIndent();
1106             ipw.println();
1107         }
1108 
1109         @Override
dump(PrintWriter pw, @Nullable String packageName)1110         void dump(PrintWriter pw, @Nullable String packageName) {
1111             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
1112             try {
1113                 ipw.println();
1114                 ipw.println("APEX session state:");
1115                 ipw.increaseIndent();
1116                 final ApexSessionInfo[] sessions = waitForApexService().getSessions();
1117                 for (ApexSessionInfo si : sessions) {
1118                     ipw.println("Session ID: " + si.sessionId);
1119                     ipw.increaseIndent();
1120                     if (si.isUnknown) {
1121                         ipw.println("State: UNKNOWN");
1122                     } else if (si.isVerified) {
1123                         ipw.println("State: VERIFIED");
1124                     } else if (si.isStaged) {
1125                         ipw.println("State: STAGED");
1126                     } else if (si.isActivated) {
1127                         ipw.println("State: ACTIVATED");
1128                     } else if (si.isActivationFailed) {
1129                         ipw.println("State: ACTIVATION FAILED");
1130                     } else if (si.isSuccess) {
1131                         ipw.println("State: SUCCESS");
1132                     } else if (si.isRevertInProgress) {
1133                         ipw.println("State: REVERT IN PROGRESS");
1134                     } else if (si.isReverted) {
1135                         ipw.println("State: REVERTED");
1136                     } else if (si.isRevertFailed) {
1137                         ipw.println("State: REVERT FAILED");
1138                     }
1139                     ipw.decreaseIndent();
1140                 }
1141                 ipw.decreaseIndent();
1142                 ipw.println();
1143                 synchronized (mLock) {
1144                     if (mAllPackagesCache == null) {
1145                         ipw.println("APEX packages have not been scanned");
1146                         return;
1147                     }
1148                 }
1149                 ipw.println("Active APEX packages:");
1150                 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
1151                 ipw.println("Inactive APEX packages:");
1152                 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
1153                 ipw.println("Factory APEX packages:");
1154                 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
1155             } catch (RemoteException e) {
1156                 ipw.println("Couldn't communicate with apexd.");
1157             }
1158         }
1159     }
1160 
1161     /**
1162      * An implementation of {@link ApexManager} that should be used in case device does not support
1163      * updating APEX packages.
1164      */
1165     private static final class ApexManagerFlattenedApex extends ApexManager {
1166         @Override
getActiveApexInfos()1167         public List<ActiveApexInfo> getActiveApexInfos() {
1168             // There is no apexd running in case of flattened apex
1169             // We look up the /apex directory and identify the active APEX modules from there.
1170             // As "preinstalled" path, we just report /system since in the case of flattened APEX
1171             // the /apex directory is just a symlink to /system/apex.
1172             List<ActiveApexInfo> result = new ArrayList<>();
1173             File apexDir = Environment.getApexDirectory();
1174             if (apexDir.isDirectory()) {
1175                 File[] files = apexDir.listFiles();
1176                 // listFiles might be null if system server doesn't have permission to read
1177                 // a directory.
1178                 if (files != null) {
1179                     for (File file : files) {
1180                         if (file.isDirectory() && !file.getName().contains("@")
1181                                 // In flattened configuration, init special-cases the art directory
1182                                 // and bind-mounts com.android.art.debug to com.android.art.
1183                                 && !file.getName().equals("com.android.art.debug")) {
1184                             result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
1185                         }
1186                     }
1187                 }
1188             }
1189             return result;
1190         }
1191 
1192         @Override
scanApexPackagesTraced(@onNull PackageParser2 packageParser, @NonNull ExecutorService executorService)1193         void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
1194                 @NonNull ExecutorService executorService) {
1195             // No-op
1196         }
1197 
1198         @Override
getPackageInfo(String packageName, int flags)1199         public PackageInfo getPackageInfo(String packageName, int flags) {
1200             return null;
1201         }
1202 
1203         @Override
getActivePackages()1204         List<PackageInfo> getActivePackages() {
1205             return Collections.emptyList();
1206         }
1207 
1208         @Override
getFactoryPackages()1209         List<PackageInfo> getFactoryPackages() {
1210             return Collections.emptyList();
1211         }
1212 
1213         @Override
getInactivePackages()1214         List<PackageInfo> getInactivePackages() {
1215             return Collections.emptyList();
1216         }
1217 
1218         @Override
isApexPackage(String packageName)1219         boolean isApexPackage(String packageName) {
1220             return false;
1221         }
1222 
1223         @Override
1224         @Nullable
getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)1225         public String getActiveApexPackageNameContainingPackage(
1226                 @NonNull String containedPackageName) {
1227             Objects.requireNonNull(containedPackageName);
1228 
1229             return null;
1230         }
1231 
1232         @Override
getStagedSessionInfo(int sessionId)1233         ApexSessionInfo getStagedSessionInfo(int sessionId) {
1234             throw new UnsupportedOperationException();
1235         }
1236 
1237         @Override
getSessions()1238         SparseArray<ApexSessionInfo> getSessions() {
1239             return new SparseArray<>(0);
1240         }
1241 
1242         @Override
submitStagedSession(ApexSessionParams params)1243         ApexInfoList submitStagedSession(ApexSessionParams params)
1244                 throws PackageManagerException {
1245             throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
1246                     "Device doesn't support updating APEX");
1247         }
1248 
1249         @Override
markStagedSessionReady(int sessionId)1250         void markStagedSessionReady(int sessionId) {
1251             throw new UnsupportedOperationException();
1252         }
1253 
1254         @Override
markStagedSessionSuccessful(int sessionId)1255         void markStagedSessionSuccessful(int sessionId) {
1256             throw new UnsupportedOperationException();
1257         }
1258 
1259         @Override
isApexSupported()1260         boolean isApexSupported() {
1261             return false;
1262         }
1263 
1264         @Override
revertActiveSessions()1265         boolean revertActiveSessions() {
1266             throw new UnsupportedOperationException();
1267         }
1268 
1269         @Override
abortStagedSession(int sessionId)1270         boolean abortStagedSession(int sessionId) {
1271             throw new UnsupportedOperationException();
1272         }
1273 
1274         @Override
uninstallApex(String apexPackagePath)1275         boolean uninstallApex(String apexPackagePath) {
1276             throw new UnsupportedOperationException();
1277         }
1278 
1279         @Override
registerApkInApex(AndroidPackage pkg)1280         void registerApkInApex(AndroidPackage pkg) {
1281             // No-op
1282         }
1283 
1284         @Override
reportErrorWithApkInApex(String scanDirPath, String errorMsg)1285         void reportErrorWithApkInApex(String scanDirPath, String errorMsg) {
1286             // No-op
1287         }
1288 
1289         @Override
1290         @Nullable
getApkInApexInstallError(String apexPackageName)1291         String getApkInApexInstallError(String apexPackageName) {
1292             return null;
1293         }
1294 
1295         @Override
getApksInApex(String apexPackageName)1296         List<String> getApksInApex(String apexPackageName) {
1297             return Collections.emptyList();
1298         }
1299 
1300         @Override
1301         @Nullable
getApexModuleNameForPackageName(String apexPackageName)1302         public String getApexModuleNameForPackageName(String apexPackageName) {
1303             return null;
1304         }
1305 
1306         @Override
snapshotCeData(int userId, int rollbackId, String apexPackageName)1307         public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
1308             throw new UnsupportedOperationException();
1309         }
1310 
1311         @Override
restoreCeData(int userId, int rollbackId, String apexPackageName)1312         public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
1313             throw new UnsupportedOperationException();
1314         }
1315 
1316         @Override
destroyDeSnapshots(int rollbackId)1317         public boolean destroyDeSnapshots(int rollbackId) {
1318             throw new UnsupportedOperationException();
1319         }
1320 
1321         @Override
destroyCeSnapshots(int userId, int rollbackId)1322         public boolean destroyCeSnapshots(int userId, int rollbackId) {
1323             return true;
1324         }
1325 
1326         @Override
destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)1327         public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
1328             return true;
1329         }
1330 
1331         @Override
markBootCompleted()1332         public void markBootCompleted() {
1333             // No-op
1334         }
1335 
1336         @Override
calculateSizeForCompressedApex(CompressedApexInfoList infoList)1337         public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) {
1338             throw new UnsupportedOperationException();
1339         }
1340 
1341         @Override
reserveSpaceForCompressedApex(CompressedApexInfoList infoList)1342         public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) {
1343             throw new UnsupportedOperationException();
1344         }
1345 
1346         @Override
installPackage(File apexFile, PackageParser2 packageParser)1347         void installPackage(File apexFile, PackageParser2 packageParser) {
1348             throw new UnsupportedOperationException("APEX updates are not supported");
1349         }
1350 
1351         @Override
dump(PrintWriter pw, String packageName)1352         void dump(PrintWriter pw, String packageName) {
1353             // No-op
1354         }
1355     }
1356 }
1357