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 android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.apex.ApexInfo;
23 import android.apex.ApexInfoList;
24 import android.apex.ApexSessionInfo;
25 import android.apex.ApexSessionParams;
26 import android.apex.IApexService;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageInstaller;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageParser;
36 import android.os.Binder;
37 import android.os.Environment;
38 import android.os.RemoteException;
39 import android.sysprop.ApexProperties;
40 import android.util.Slog;
41 
42 import com.android.internal.annotations.GuardedBy;
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.os.BackgroundThread;
45 import com.android.internal.util.IndentingPrintWriter;
46 
47 import java.io.File;
48 import java.io.PrintWriter;
49 import java.lang.annotation.Retention;
50 import java.lang.annotation.RetentionPolicy;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.HashSet;
55 import java.util.List;
56 import java.util.stream.Collectors;
57 
58 /**
59  * ApexManager class handles communications with the apex service to perform operation and queries,
60  * as well as providing caching to avoid unnecessary calls to the service.
61  */
62 abstract class ApexManager {
63 
64     private static final String TAG = "ApexManager";
65 
66     static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
67     static final int MATCH_FACTORY_PACKAGE = 1 << 1;
68 
69     /**
70      * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
71      * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
72      * evaluates to {@code true}.
73      */
create(Context systemContext)74     static ApexManager create(Context systemContext) {
75         if (ApexProperties.updatable().orElse(false)) {
76             return new ApexManagerImpl(systemContext);
77         } else {
78             return new ApexManagerFlattenedApex();
79         }
80     }
81 
82     /**
83      * Minimal information about APEX mount points and the original APEX package they refer to.
84      */
85     static class ActiveApexInfo {
86         public final File apexDirectory;
87         public final File preinstalledApexPath;
88 
ActiveApexInfo(File apexDirectory, File preinstalledApexPath)89         private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) {
90             this.apexDirectory = apexDirectory;
91             this.preinstalledApexPath = preinstalledApexPath;
92         }
93     }
94 
95     /**
96      * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
97      */
getActiveApexInfos()98     abstract List<ActiveApexInfo> getActiveApexInfos();
99 
systemReady()100     abstract void systemReady();
101 
102     /**
103      * Retrieves information about an APEX package.
104      *
105      * @param packageName the package name to look for. Note that this is the package name reported
106      *                    in the APK container manifest (i.e. AndroidManifest.xml), which might
107      *                    differ from the one reported in the APEX manifest (i.e.
108      *                    apex_manifest.json).
109      * @param flags the type of package to return. This may match to active packages
110      *              and factory (pre-installed) packages.
111      * @return a PackageInfo object with the information about the package, or null if the package
112      *         is not found.
113      */
114     @Nullable
getPackageInfo(String packageName, @PackageInfoFlags int flags)115     abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
116 
117     /**
118      * Retrieves information about all active APEX packages.
119      *
120      * @return a List of PackageInfo object, each one containing information about a different
121      *         active package.
122      */
getActivePackages()123     abstract List<PackageInfo> getActivePackages();
124 
125     /**
126      * Retrieves information about all active pre-installed APEX packages.
127      *
128      * @return a List of PackageInfo object, each one containing information about a different
129      *         active pre-installed package.
130      */
getFactoryPackages()131     abstract List<PackageInfo> getFactoryPackages();
132 
133     /**
134      * Retrieves information about all inactive APEX packages.
135      *
136      * @return a List of PackageInfo object, each one containing information about a different
137      *         inactive package.
138      */
getInactivePackages()139     abstract List<PackageInfo> getInactivePackages();
140 
141     /**
142      * Checks if {@code packageName} is an apex package.
143      *
144      * @param packageName package to check.
145      * @return {@code true} if {@code packageName} is an apex package.
146      */
isApexPackage(String packageName)147     abstract boolean isApexPackage(String packageName);
148 
149     /**
150      * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
151      * track the different states of a session.
152      *
153      * @param sessionId the identifier of the session.
154      * @return an ApexSessionInfo object, or null if the session is not known.
155      */
156     @Nullable
getStagedSessionInfo(int sessionId)157     abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
158 
159     /**
160      * Submit a staged session to apex service. This causes the apex service to perform some initial
161      * verification and accept or reject the session. Submitting a session successfully is not
162      * enough for it to be activated at the next boot, the caller needs to call
163      * {@link #markStagedSessionReady(int)}.
164      *
165      * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted.
166      * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain
167      *                        an array of identifiers of all the child sessions. Otherwise it should
168      *                        be an empty array.
169      * @throws PackageManagerException if call to apexd fails
170      */
submitStagedSession(int sessionId, @NonNull int[] childSessionIds)171     abstract ApexInfoList submitStagedSession(int sessionId, @NonNull int[] childSessionIds)
172             throws PackageManagerException;
173 
174     /**
175      * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
176      * applied at next reboot.
177      *
178      * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
179      * @throws PackageManagerException if call to apexd fails
180      */
markStagedSessionReady(int sessionId)181     abstract void markStagedSessionReady(int sessionId) throws PackageManagerException;
182 
183     /**
184      * Marks a staged session as successful.
185      *
186      * <p>Only activated session can be marked as successful.
187      *
188      * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
189      *                  successful.
190      */
markStagedSessionSuccessful(int sessionId)191     abstract void markStagedSessionSuccessful(int sessionId);
192 
193     /**
194      * Whether the current device supports the management of APEX packages.
195      *
196      * @return true if APEX packages can be managed on this device, false otherwise.
197      */
isApexSupported()198     abstract boolean isApexSupported();
199 
200     /**
201      * Abandons the (only) active session previously submitted.
202      *
203      * @return {@code true} upon success, {@code false} if any remote exception occurs
204      */
revertActiveSessions()205     abstract boolean revertActiveSessions();
206 
207     /**
208      * Abandons the staged session with the given sessionId.
209      *
210      * @return {@code true} upon success, {@code false} if any remote exception occurs
211      */
abortStagedSession(int sessionId)212     abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
213 
214     /**
215      * Uninstalls given {@code apexPackage}.
216      *
217      * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
218      *
219      * @param apexPackagePath package to uninstall.
220      * @return {@code true} upon successful uninstall, {@code false} otherwise.
221      */
uninstallApex(String apexPackagePath)222     abstract boolean uninstallApex(String apexPackagePath);
223 
224     /**
225      * Dumps various state information to the provided {@link PrintWriter} object.
226      *
227      * @param pw the {@link PrintWriter} object to send information to.
228      * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
229      *                    information about that specific package will be dumped.
230      */
dump(PrintWriter pw, @Nullable String packageName)231     abstract void dump(PrintWriter pw, @Nullable String packageName);
232 
233     @IntDef(
234             flag = true,
235             prefix = { "MATCH_"},
236             value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
237     @Retention(RetentionPolicy.SOURCE)
238     @interface PackageInfoFlags{}
239 
240     /**
241      * An implementation of {@link ApexManager} that should be used in case device supports updating
242      * APEX packages.
243      */
244     @VisibleForTesting
245     protected static class ApexManagerImpl extends ApexManager {
246         private final Context mContext;
247         private final Object mLock = new Object();
248         /**
249          * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code
250          * AndroidManifest.xml}
251          *
252          * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
253          * AndroidManifest.xml}.
254           */
255         @GuardedBy("mLock")
256         private List<PackageInfo> mAllPackagesCache;
257 
ApexManagerImpl(Context context)258         ApexManagerImpl(Context context) {
259             mContext = context;
260         }
261 
262         /**
263          * Whether an APEX package is active or not.
264          *
265          * @param packageInfo the package to check
266          * @return {@code true} if this package is active, {@code false} otherwise.
267          */
isActive(PackageInfo packageInfo)268         private static boolean isActive(PackageInfo packageInfo) {
269             return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
270         }
271 
272         /**
273          * Whether the APEX package is pre-installed or not.
274          *
275          * @param packageInfo the package to check
276          * @return {@code true} if this package is pre-installed, {@code false} otherwise.
277          */
isFactory(PackageInfo packageInfo)278         private static boolean isFactory(PackageInfo packageInfo) {
279             return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
280         }
281 
282         /**
283          * Retrieve the service from ServiceManager. If the service is not running, it will be
284          * started, and this function will block until it is ready.
285          */
286         @VisibleForTesting
waitForApexService()287         protected IApexService waitForApexService() {
288             try {
289                 return IApexService.Stub.asInterface(Binder.waitForService("apexservice"));
290             } catch (RemoteException e) {
291                 throw new IllegalStateException("Required service apexservice not available");
292             }
293         }
294 
295         @Override
getActiveApexInfos()296         List<ActiveApexInfo> getActiveApexInfos() {
297             try {
298                 return Arrays.stream(waitForApexService().getActivePackages())
299                         .map(apexInfo -> new ActiveApexInfo(
300                                 new File(
301                                 Environment.getApexDirectory() + File.separator
302                                         + apexInfo.moduleName),
303                                 new File(apexInfo.modulePath))).collect(
304                                 Collectors.toList());
305             } catch (RemoteException e) {
306                 Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
307             }
308             return Collections.emptyList();
309         }
310 
311         @Override
systemReady()312         void systemReady() {
313             mContext.registerReceiver(new BroadcastReceiver() {
314                 @Override
315                 public void onReceive(Context context, Intent intent) {
316                     // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
317                     // expensive to run it in broadcast handler thread.
318                     BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
319                     mContext.unregisterReceiver(this);
320                 }
321             }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
322         }
323 
populateAllPackagesCacheIfNeeded()324         private void populateAllPackagesCacheIfNeeded() {
325             synchronized (mLock) {
326                 if (mAllPackagesCache != null) {
327                     return;
328                 }
329                 try {
330                     mAllPackagesCache = new ArrayList<>();
331                     HashSet<String> activePackagesSet = new HashSet<>();
332                     HashSet<String> factoryPackagesSet = new HashSet<>();
333                     final ApexInfo[] allPkgs = waitForApexService().getAllPackages();
334                     for (ApexInfo ai : allPkgs) {
335                         // If the device is using flattened APEX, don't report any APEX
336                         // packages since they won't be managed or updated by PackageManager.
337                         if ((new File(ai.modulePath)).isDirectory()) {
338                             break;
339                         }
340                         int flags = PackageManager.GET_META_DATA
341                                 | PackageManager.GET_SIGNING_CERTIFICATES
342                                 | PackageManager.GET_SIGNATURES;
343                         PackageParser.Package pkg;
344                         try {
345                             File apexFile = new File(ai.modulePath);
346                             PackageParser pp = new PackageParser();
347                             pkg = pp.parsePackage(apexFile, flags, false);
348                             PackageParser.collectCertificates(pkg, false);
349                         } catch (PackageParser.PackageParserException pe) {
350                             throw new IllegalStateException("Unable to parse: " + ai, pe);
351                         }
352 
353                         final PackageInfo packageInfo =
354                                 PackageParser.generatePackageInfo(pkg, ai, flags);
355                         mAllPackagesCache.add(packageInfo);
356                         if (ai.isActive) {
357                             if (activePackagesSet.contains(packageInfo.packageName)) {
358                                 throw new IllegalStateException(
359                                         "Two active packages have the same name: "
360                                                 + packageInfo.packageName);
361                             }
362                             activePackagesSet.add(packageInfo.packageName);
363                         }
364                         if (ai.isFactory) {
365                             if (factoryPackagesSet.contains(packageInfo.packageName)) {
366                                 throw new IllegalStateException(
367                                         "Two factory packages have the same name: "
368                                                 + packageInfo.packageName);
369                             }
370                             factoryPackagesSet.add(packageInfo.packageName);
371                         }
372 
373                     }
374                 } catch (RemoteException re) {
375                     Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
376                     throw new RuntimeException(re);
377                 }
378             }
379         }
380 
381         @Override
getPackageInfo(String packageName, @PackageInfoFlags int flags)382         @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
383             populateAllPackagesCacheIfNeeded();
384             boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
385             boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
386             for (PackageInfo packageInfo: mAllPackagesCache) {
387                 if (!packageInfo.packageName.equals(packageName)) {
388                     continue;
389                 }
390                 if ((matchActive && isActive(packageInfo))
391                         || (matchFactory && isFactory(packageInfo))) {
392                     return packageInfo;
393                 }
394             }
395             return null;
396         }
397 
398         @Override
getActivePackages()399         List<PackageInfo> getActivePackages() {
400             populateAllPackagesCacheIfNeeded();
401             return mAllPackagesCache
402                     .stream()
403                     .filter(item -> isActive(item))
404                     .collect(Collectors.toList());
405         }
406 
407         @Override
getFactoryPackages()408         List<PackageInfo> getFactoryPackages() {
409             populateAllPackagesCacheIfNeeded();
410             return mAllPackagesCache
411                     .stream()
412                     .filter(item -> isFactory(item))
413                     .collect(Collectors.toList());
414         }
415 
416         @Override
getInactivePackages()417         List<PackageInfo> getInactivePackages() {
418             populateAllPackagesCacheIfNeeded();
419             return mAllPackagesCache
420                     .stream()
421                     .filter(item -> !isActive(item))
422                     .collect(Collectors.toList());
423         }
424 
425         @Override
isApexPackage(String packageName)426         boolean isApexPackage(String packageName) {
427             if (!isApexSupported()) return false;
428             populateAllPackagesCacheIfNeeded();
429             for (PackageInfo packageInfo : mAllPackagesCache) {
430                 if (packageInfo.packageName.equals(packageName)) {
431                     return true;
432                 }
433             }
434             return false;
435         }
436 
437         @Override
getStagedSessionInfo(int sessionId)438         @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
439             try {
440                 ApexSessionInfo apexSessionInfo =
441                         waitForApexService().getStagedSessionInfo(sessionId);
442                 if (apexSessionInfo.isUnknown) {
443                     return null;
444                 }
445                 return apexSessionInfo;
446             } catch (RemoteException re) {
447                 Slog.e(TAG, "Unable to contact apexservice", re);
448                 throw new RuntimeException(re);
449             }
450         }
451 
452         @Override
submitStagedSession(int sessionId, @NonNull int[] childSessionIds)453         ApexInfoList submitStagedSession(int sessionId, @NonNull int[] childSessionIds)
454                 throws PackageManagerException {
455             try {
456                 final ApexInfoList apexInfoList = new ApexInfoList();
457                 ApexSessionParams params = new ApexSessionParams();
458                 params.sessionId = sessionId;
459                 params.childSessionIds = childSessionIds;
460                 waitForApexService().submitStagedSession(params, apexInfoList);
461                 return apexInfoList;
462             } catch (RemoteException re) {
463                 Slog.e(TAG, "Unable to contact apexservice", re);
464                 throw new RuntimeException(re);
465             } catch (Exception e) {
466                 throw new PackageManagerException(
467                         PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
468                         "apexd verification failed : " + e.getMessage());
469             }
470         }
471 
472         @Override
markStagedSessionReady(int sessionId)473         void markStagedSessionReady(int sessionId) throws PackageManagerException {
474             try {
475                 waitForApexService().markStagedSessionReady(sessionId);
476             } catch (RemoteException re) {
477                 Slog.e(TAG, "Unable to contact apexservice", re);
478                 throw new RuntimeException(re);
479             } catch (Exception e) {
480                 throw new PackageManagerException(
481                         PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
482                         "Failed to mark apexd session as ready : " + e.getMessage());
483             }
484         }
485 
486         @Override
markStagedSessionSuccessful(int sessionId)487         void markStagedSessionSuccessful(int sessionId) {
488             try {
489                 waitForApexService().markStagedSessionSuccessful(sessionId);
490             } catch (RemoteException re) {
491                 Slog.e(TAG, "Unable to contact apexservice", re);
492                 throw new RuntimeException(re);
493             } catch (Exception e) {
494                 // It is fine to just log an exception in this case. APEXd will be able to recover
495                 // in case markStagedSessionSuccessful fails.
496                 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
497             }
498         }
499 
500         @Override
isApexSupported()501         boolean isApexSupported() {
502             return true;
503         }
504 
505         @Override
revertActiveSessions()506         boolean revertActiveSessions() {
507             try {
508                 waitForApexService().revertActiveSessions();
509                 return true;
510             } catch (RemoteException re) {
511                 Slog.e(TAG, "Unable to contact apexservice", re);
512                 return false;
513             } catch (Exception e) {
514                 Slog.e(TAG, e.getMessage(), e);
515                 return false;
516             }
517         }
518 
519         @Override
abortStagedSession(int sessionId)520         boolean abortStagedSession(int sessionId) throws PackageManagerException {
521             try {
522                 waitForApexService().abortStagedSession(sessionId);
523                 return true;
524             } catch (RemoteException re) {
525                 Slog.e(TAG, "Unable to contact apexservice", re);
526                 return false;
527             } catch (Exception e) {
528                 throw new PackageManagerException(
529                         PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
530                         "Failed to abort staged session : " + e.getMessage());
531             }
532         }
533 
534         @Override
uninstallApex(String apexPackagePath)535         boolean uninstallApex(String apexPackagePath) {
536             try {
537                 waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
538                 return true;
539             } catch (Exception e) {
540                 return false;
541             }
542         }
543 
544         /**
545          * Dump information about the packages contained in a particular cache
546          * @param packagesCache the cache to print information about.
547          * @param packageName a {@link String} containing a package name, or {@code null}. If set,
548          *                    only information about that specific package will be dumped.
549          * @param ipw the {@link IndentingPrintWriter} object to send information to.
550          */
dumpFromPackagesCache( List<PackageInfo> packagesCache, @Nullable String packageName, IndentingPrintWriter ipw)551         void dumpFromPackagesCache(
552                 List<PackageInfo> packagesCache,
553                 @Nullable String packageName,
554                 IndentingPrintWriter ipw) {
555             ipw.println();
556             ipw.increaseIndent();
557             for (PackageInfo pi : packagesCache) {
558                 if (packageName != null && !packageName.equals(pi.packageName)) {
559                     continue;
560                 }
561                 ipw.println(pi.packageName);
562                 ipw.increaseIndent();
563                 ipw.println("Version: " + pi.versionCode);
564                 ipw.println("Path: " + pi.applicationInfo.sourceDir);
565                 ipw.println("IsActive: " + isActive(pi));
566                 ipw.println("IsFactory: " + isFactory(pi));
567                 ipw.decreaseIndent();
568             }
569             ipw.decreaseIndent();
570             ipw.println();
571         }
572 
573         @Override
dump(PrintWriter pw, @Nullable String packageName)574         void dump(PrintWriter pw, @Nullable String packageName) {
575             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
576             try {
577                 populateAllPackagesCacheIfNeeded();
578                 ipw.println();
579                 ipw.println("Active APEX packages:");
580                 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
581                 ipw.println("Inactive APEX packages:");
582                 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
583                 ipw.println("Factory APEX packages:");
584                 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
585                 ipw.increaseIndent();
586                 ipw.println("APEX session state:");
587                 ipw.increaseIndent();
588                 final ApexSessionInfo[] sessions = waitForApexService().getSessions();
589                 for (ApexSessionInfo si : sessions) {
590                     ipw.println("Session ID: " + si.sessionId);
591                     ipw.increaseIndent();
592                     if (si.isUnknown) {
593                         ipw.println("State: UNKNOWN");
594                     } else if (si.isVerified) {
595                         ipw.println("State: VERIFIED");
596                     } else if (si.isStaged) {
597                         ipw.println("State: STAGED");
598                     } else if (si.isActivated) {
599                         ipw.println("State: ACTIVATED");
600                     } else if (si.isActivationFailed) {
601                         ipw.println("State: ACTIVATION FAILED");
602                     } else if (si.isSuccess) {
603                         ipw.println("State: SUCCESS");
604                     } else if (si.isRevertInProgress) {
605                         ipw.println("State: REVERT IN PROGRESS");
606                     } else if (si.isReverted) {
607                         ipw.println("State: REVERTED");
608                     } else if (si.isRevertFailed) {
609                         ipw.println("State: REVERT FAILED");
610                     }
611                     ipw.decreaseIndent();
612                 }
613                 ipw.decreaseIndent();
614             } catch (RemoteException e) {
615                 ipw.println("Couldn't communicate with apexd.");
616             }
617         }
618     }
619 
620     /**
621      * An implementation of {@link ApexManager} that should be used in case device does not support
622      * updating APEX packages.
623      */
624     private static final class ApexManagerFlattenedApex extends ApexManager {
625 
626         @Override
getActiveApexInfos()627         List<ActiveApexInfo> getActiveApexInfos() {
628             // There is no apexd running in case of flattened apex
629             // We look up the /apex directory and identify the active APEX modules from there.
630             // As "preinstalled" path, we just report /system since in the case of flattened APEX
631             // the /apex directory is just a symlink to /system/apex.
632             List<ActiveApexInfo> result = new ArrayList<>();
633             File apexDir = Environment.getApexDirectory();
634             // In flattened configuration, init special-case the art directory and bind-mounts
635             // com.android.art.{release|debug} to com.android.art. At the time of writing, these
636             // directories are copied from the kArtApexDirNames variable in
637             // system/core/init/mount_namespace.cpp.
638             String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
639             if (apexDir.isDirectory()) {
640                 File[] files = apexDir.listFiles();
641                 // listFiles might be null if system server doesn't have permission to read
642                 // a directory.
643                 if (files != null) {
644                     for (File file : files) {
645                         if (file.isDirectory() && !file.getName().contains("@")) {
646                             for (String skipDir : skipDirs) {
647                                 if (file.getName().equals(skipDir)) {
648                                     continue;
649                                 }
650                             }
651                             result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
652                         }
653                     }
654                 }
655             }
656             return result;
657         }
658 
659         @Override
systemReady()660         void systemReady() {
661             // No-op
662         }
663 
664         @Override
getPackageInfo(String packageName, int flags)665         PackageInfo getPackageInfo(String packageName, int flags) {
666             return null;
667         }
668 
669         @Override
getActivePackages()670         List<PackageInfo> getActivePackages() {
671             return Collections.emptyList();
672         }
673 
674         @Override
getFactoryPackages()675         List<PackageInfo> getFactoryPackages() {
676             return Collections.emptyList();
677         }
678 
679         @Override
getInactivePackages()680         List<PackageInfo> getInactivePackages() {
681             return Collections.emptyList();
682         }
683 
684         @Override
isApexPackage(String packageName)685         boolean isApexPackage(String packageName) {
686             return false;
687         }
688 
689         @Override
getStagedSessionInfo(int sessionId)690         ApexSessionInfo getStagedSessionInfo(int sessionId) {
691             throw new UnsupportedOperationException();
692         }
693 
694         @Override
submitStagedSession(int sessionId, int[] childSessionIds)695         ApexInfoList submitStagedSession(int sessionId, int[] childSessionIds)
696                 throws PackageManagerException {
697             throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
698                     "Device doesn't support updating APEX");
699         }
700 
701         @Override
markStagedSessionReady(int sessionId)702         void markStagedSessionReady(int sessionId) {
703             throw new UnsupportedOperationException();
704         }
705 
706         @Override
markStagedSessionSuccessful(int sessionId)707         void markStagedSessionSuccessful(int sessionId) {
708             throw new UnsupportedOperationException();
709         }
710 
711         @Override
isApexSupported()712         boolean isApexSupported() {
713             return false;
714         }
715 
716         @Override
revertActiveSessions()717         boolean revertActiveSessions() {
718             throw new UnsupportedOperationException();
719         }
720 
721         @Override
abortStagedSession(int sessionId)722         boolean abortStagedSession(int sessionId) throws PackageManagerException {
723             throw new UnsupportedOperationException();
724         }
725 
726         @Override
uninstallApex(String apexPackagePath)727         boolean uninstallApex(String apexPackagePath) {
728             throw new UnsupportedOperationException();
729         }
730 
731         @Override
dump(PrintWriter pw, String packageName)732         void dump(PrintWriter pw, String packageName) {
733             // No-op
734         }
735     }
736 }
737