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.CompressedApexInfoList;
27 import android.apex.IApexService;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.SigningDetails;
31 import android.content.pm.parsing.result.ParseResult;
32 import android.content.pm.parsing.result.ParseTypeImpl;
33 import android.os.Binder;
34 import android.os.Environment;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.os.Trace;
38 import android.text.TextUtils;
39 import android.util.ArrayMap;
40 import android.util.ArraySet;
41 import android.util.Singleton;
42 import android.util.Slog;
43 import android.util.SparseArray;
44 import android.util.apk.ApkSignatureVerifier;
45 
46 import com.android.internal.annotations.GuardedBy;
47 import com.android.internal.annotations.VisibleForTesting;
48 import com.android.internal.pm.pkg.component.ParsedApexSystemService;
49 import com.android.internal.util.IndentingPrintWriter;
50 import com.android.internal.util.Preconditions;
51 import com.android.modules.utils.build.UnboundedSdkLevel;
52 import com.android.server.pm.pkg.AndroidPackage;
53 import com.android.server.utils.TimingsTraceAndSlog;
54 
55 import com.google.android.collect.Lists;
56 
57 import java.io.File;
58 import java.io.PrintWriter;
59 import java.lang.annotation.Retention;
60 import java.lang.annotation.RetentionPolicy;
61 import java.nio.file.Path;
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.Objects;
67 import java.util.Set;
68 
69 /**
70  * ApexManager class handles communications with the apex service to perform operation and queries,
71  * as well as providing caching to avoid unnecessary calls to the service.
72  */
73 public abstract class ApexManager {
74 
75     private static final String TAG = "ApexManager";
76 
77     public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
78     static final int MATCH_FACTORY_PACKAGE = 1 << 1;
79 
80     private static final Singleton<ApexManager> sApexManagerSingleton =
81             new Singleton<ApexManager>() {
82                 @Override
83                 protected ApexManager create() {
84                     return new ApexManagerImpl();
85                 }
86             };
87 
88     /**
89      * Returns an instance of {@link ApexManagerImpl}
90      * @hide
91      */
getInstance()92     public static ApexManager getInstance() {
93         return sApexManagerSingleton.get();
94     }
95 
96     static class ScanResult {
97         public final ApexInfo apexInfo;
98         public final AndroidPackage pkg;
99         public final String packageName;
ScanResult(ApexInfo apexInfo, AndroidPackage pkg, String packageName)100         ScanResult(ApexInfo apexInfo, AndroidPackage pkg, String packageName) {
101             this.apexInfo = apexInfo;
102             this.pkg = pkg;
103             this.packageName = packageName;
104         }
105     }
106 
107     /**
108      * Minimal information about APEX mount points and the original APEX package they refer to.
109      * @hide
110      */
111     public static class ActiveApexInfo {
112         @Nullable public final String apexModuleName;
113         public final File apexDirectory;
114         public final File preInstalledApexPath;
115         public final boolean isFactory;
116         public final File apexFile;
117         public final boolean activeApexChanged;
118 
ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile)119         private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) {
120             this(null, apexDirectory, preInstalledApexPath, true, apexFile, false);
121         }
122 
ActiveApexInfo(@ullable String apexModuleName, File apexDirectory, File preInstalledApexPath, boolean isFactory, File apexFile, boolean activeApexChanged)123         private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
124                 File preInstalledApexPath, boolean isFactory, File apexFile,
125                 boolean activeApexChanged) {
126             this.apexModuleName = apexModuleName;
127             this.apexDirectory = apexDirectory;
128             this.preInstalledApexPath = preInstalledApexPath;
129             this.isFactory = isFactory;
130             this.apexFile = apexFile;
131             this.activeApexChanged = activeApexChanged;
132         }
133 
ActiveApexInfo(ApexInfo apexInfo)134         public ActiveApexInfo(ApexInfo apexInfo) {
135             this(
136                     apexInfo.moduleName,
137                     new File(Environment.getApexDirectory() + File.separator
138                             + apexInfo.moduleName),
139                     new File(apexInfo.preinstalledModulePath),
140                     apexInfo.isFactory,
141                     new File(apexInfo.modulePath),
142                     apexInfo.activeApexChanged);
143         }
144     }
145 
getAllApexInfos()146     abstract ApexInfo[] getAllApexInfos();
notifyScanResult(List<ScanResult> scanResults)147     abstract void notifyScanResult(List<ScanResult> scanResults);
148 
149     /**
150      * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
151      *
152      * @hide
153      */
getActiveApexInfos()154     public abstract List<ActiveApexInfo> getActiveApexInfos();
155 
156     /**
157      * Returns the active apex package's name that contains the (apk) package.
158      *
159      * @param containedPackageName The (apk) package that might be in a apex
160      * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside
161      *         any apex.
162      */
163     @Nullable
getActiveApexPackageNameContainingPackage( @onNull String containedPackageName)164     public abstract String getActiveApexPackageNameContainingPackage(
165             @NonNull String containedPackageName);
166 
167     /**
168      * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
169      * track the different states of a session.
170      *
171      * @param sessionId the identifier of the session.
172      * @return an ApexSessionInfo object, or null if the session is not known.
173      */
174     @Nullable
getStagedSessionInfo(int sessionId)175     abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
176 
177     /**
178      * Returns array of all staged sessions known to apexd.
179      */
180     @NonNull
getSessions()181     abstract SparseArray<ApexSessionInfo> getSessions();
182 
183     /**
184      * Submit a staged session to apex service. This causes the apex service to perform some initial
185      * verification and accept or reject the session. Submitting a session successfully is not
186      * enough for it to be activated at the next boot, the caller needs to call
187      * {@link #markStagedSessionReady(int)}.
188      *
189      * @throws PackageManagerException if call to apexd fails
190      */
submitStagedSession(ApexSessionParams params)191     abstract ApexInfoList submitStagedSession(ApexSessionParams params)
192             throws PackageManagerException;
193 
194     /**
195      * Returns {@code ApeInfo} about apex sessions that have been marked ready via
196      * {@link #markStagedSessionReady(int)}
197      *
198      * Returns empty array if there is no staged apex session or if there is any error.
199      */
getStagedApexInfos(ApexSessionParams params)200     abstract ApexInfo[] getStagedApexInfos(ApexSessionParams params);
201 
202     /**
203      * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
204      * applied at next reboot.
205      *
206      * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
207      * @throws PackageManagerException if call to apexd fails
208      */
markStagedSessionReady(int sessionId)209     abstract void markStagedSessionReady(int sessionId) throws PackageManagerException;
210 
211     /**
212      * Marks a staged session as successful.
213      *
214      * <p>Only activated session can be marked as successful.
215      *
216      * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
217      *                  successful.
218      */
markStagedSessionSuccessful(int sessionId)219     abstract void markStagedSessionSuccessful(int sessionId);
220 
221     /**
222      * Whether the current device supports the management of APEX packages.
223      *
224      * @return true if APEX packages can be managed on this device, false otherwise.
225      */
isApexSupported()226     abstract boolean isApexSupported();
227 
228     /**
229      * Abandons the (only) active session previously submitted.
230      *
231      * @return {@code true} upon success, {@code false} if any remote exception occurs
232      */
revertActiveSessions()233     abstract boolean revertActiveSessions();
234 
235     /**
236      * Abandons the staged session with the given sessionId. Client should handle {@code false}
237      * return value carefully as failure here can leave device in inconsistent state.
238      *
239      * @return {@code true} upon success, {@code false} if any exception occurs
240      */
abortStagedSession(int sessionId)241     abstract boolean abortStagedSession(int sessionId);
242 
243     /**
244      * Uninstalls given {@code apexPackage}.
245      *
246      * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
247      *
248      * @param apexPackagePath package to uninstall.
249      * @return {@code true} upon successful uninstall, {@code false} otherwise.
250      */
uninstallApex(String apexPackagePath)251     abstract boolean uninstallApex(String apexPackagePath);
252 
253     /**
254      * Registers an APK package as an embedded apk of apex.
255      */
registerApkInApex(AndroidPackage pkg)256     abstract void registerApkInApex(AndroidPackage pkg);
257 
258     /**
259      * Reports error raised during installation of apk-in-apex.
260      *
261      * @param scanDirPath the directory of the apex inside which apk-in-apex resides.
262      * @param errorMsg the actual error that occurred when scanning the path
263      */
reportErrorWithApkInApex(String scanDirPath, String errorMsg)264     abstract void reportErrorWithApkInApex(String scanDirPath, String errorMsg);
265 
266     /**
267      * Returns null if there were no errors when installing apk-in-apex inside
268      * {@param apexPackageName}, otherwise returns the error as string
269      *
270      * @param apexPackageName Package name of the apk container of apex
271      */
272     @Nullable
getApkInApexInstallError(String apexPackageName)273     abstract String getApkInApexInstallError(String apexPackageName);
274 
275     /**
276      * Returns list of {@code packageName} of apks inside the given apex.
277      * @param apexPackageName Package name of the apk container of apex
278      */
279     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
getApksInApex(String apexPackageName)280     public abstract List<String> getApksInApex(String apexPackageName);
281 
282     /**
283      * Returns the apex module name for the given package name, if the package is an APEX. Otherwise
284      * returns {@code null}.
285      */
286     @Nullable
getApexModuleNameForPackageName(String apexPackageName)287     public abstract String getApexModuleNameForPackageName(String apexPackageName);
288 
289     /**
290      * Returns the package name of the active APEX whose name is {@code apexModuleName}. If not
291      * found, returns {@code null}.
292      */
293     @Nullable
getActivePackageNameForApexModuleName(String apexModuleName)294     public abstract String getActivePackageNameForApexModuleName(String apexModuleName);
295 
296     /**
297      * Copies the CE apex data directory for the given {@code userId} to a backup location, for use
298      * in case of rollback.
299      *
300      * @return boolean true if the snapshot was successful
301      */
snapshotCeData(int userId, int rollbackId, String apexPackageName)302     public abstract boolean snapshotCeData(int userId, int rollbackId, String apexPackageName);
303 
304     /**
305      * Restores the snapshot of the CE apex data directory for the given {@code userId}.
306      * Note the snapshot will be deleted after restoration succeeded.
307      *
308      * @return boolean true if the restore was successful
309      */
restoreCeData(int userId, int rollbackId, String apexPackageName)310     public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName);
311 
312     /**
313      * Deletes snapshots of the device encrypted apex data directories for the given
314      * {@code rollbackId}.
315      *
316      * @return boolean true if the delete was successful
317      */
destroyDeSnapshots(int rollbackId)318     public abstract boolean destroyDeSnapshots(int rollbackId);
319 
320     /**
321      *  Deletes snapshots of the credential encrypted apex data directories for the specified user,
322      *  for the given rollback id as long as the user is credential unlocked.
323      *
324      * @return boolean true if the delete was successful
325      */
destroyCeSnapshots(int userId, int rollbackId)326     public abstract boolean destroyCeSnapshots(int userId, int rollbackId);
327 
328     /**
329      * Deletes snapshots of the credential encrypted apex data directories for the specified user,
330      * where the rollback id is not included in {@code retainRollbackIds} as long as the user is
331      * credential unlocked.
332      *
333      * @return boolean true if the delete was successful
334      */
destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)335     public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
336 
337     /**
338      * Inform apexd that the boot has completed.
339      */
markBootCompleted()340     public abstract void markBootCompleted();
341 
342     /**
343      * Estimate how much storage space is needed on /data/ for decompressing apexes
344      * @param infoList List of apexes that are compressed in target build.
345      * @return Size, in bytes, the amount of space needed on /data/
346      */
calculateSizeForCompressedApex(CompressedApexInfoList infoList)347     public abstract long calculateSizeForCompressedApex(CompressedApexInfoList infoList)
348             throws RemoteException;
349 
350     /**
351      * Reserve space on /data so that apexes can be decompressed after OTA
352      * @param infoList List of apexes that are compressed in target build.
353      */
reserveSpaceForCompressedApex(CompressedApexInfoList infoList)354     public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList)
355             throws RemoteException;
356 
357     /**
358      * Performs a non-staged install of the given {@code apexFile}.
359      *
360      * If {@code force} is {@code true}, then  update is forced even for APEXes that do not support
361      * non-staged update. This feature is only available on debuggable builds to improve development
362      * velocity of the teams that have their code packaged in an APEX.
363      *
364      * @return {@code ApeInfo} about the newly installed APEX package.
365      */
installPackage(File apexFile, boolean force)366     abstract ApexInfo installPackage(File apexFile, boolean force) throws PackageManagerException;
367 
368     /**
369      * Get a list of apex system services implemented in an apex.
370      *
371      * <p>The list is sorted by initOrder for consistency.
372      */
getApexSystemServices()373     public abstract List<ApexSystemServiceInfo> getApexSystemServices();
374 
375     /**
376      * Returns an APEX file backing the mount point {@code file} is located on, or {@code null} if
377      * {@code file} doesn't belong to a {@code /apex} mount point.
378      *
379      * <p>Also returns {@code null} if device doesn't support updatable APEX packages.
380      */
381     @Nullable
getBackingApexFile(@onNull File file)382     public abstract File getBackingApexFile(@NonNull File file);
383 
384     /**
385      * Dumps various state information to the provided {@link PrintWriter} object.
386      *
387      * @param pw the {@link PrintWriter} object to send information to.
388      */
dump(PrintWriter pw)389     abstract void dump(PrintWriter pw);
390 
391     @IntDef(
392             flag = true,
393             prefix = { "MATCH_"},
394             value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
395     @Retention(RetentionPolicy.SOURCE)
396     @interface PackageInfoFlags{}
397 
398     /**
399      * An implementation of {@link ApexManager} that should be used in case device supports updating
400      * APEX packages.
401      */
402     @VisibleForTesting
403     protected static class ApexManagerImpl extends ApexManager {
404         private final Object mLock = new Object();
405 
406         // TODO(ioffe): this should be either List or ArrayMap.
407         @GuardedBy("mLock")
408         private Set<ActiveApexInfo> mActiveApexInfosCache;
409 
410         /**
411          * Map of all apex system services to the jar files they are contained in.
412          */
413         @GuardedBy("mLock")
414         private final List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
415 
416         /**
417          * Contains the list of {@code packageName}s of apks-in-apex for given
418          * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
419          * difference between {@code packageName} and {@code apexModuleName}.
420          */
421         @GuardedBy("mLock")
422         private final ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
423 
424         /**
425          * Contains the list of {@code Exception}s that were raised when installing apk-in-apex
426          * inside {@code apexModuleName}.
427          */
428         @GuardedBy("mLock")
429         private final Map<String, String> mErrorWithApkInApex = new ArrayMap<>();
430 
431         /**
432          * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
433          * apk container has a reference name, called {@code packageName}, which is found inside the
434          * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference
435          * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file.
436          *
437          * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of
438          * the apk container to {@code apexModuleName} of the apex-payload inside.
439          */
440         @GuardedBy("mLock")
441         private ArrayMap<String, String> mPackageNameToApexModuleName;
442 
443         /**
444          * Reverse mapping of {@link #mPackageNameToApexModuleName}, for active packages only.
445          */
446         @GuardedBy("mLock")
447         private ArrayMap<String, String> mApexModuleNameToActivePackageName;
448 
449         /**
450          * Retrieve the service from ServiceManager. If the service is not running, it will be
451          * started, and this function will block until it is ready.
452          */
453         @VisibleForTesting
waitForApexService()454         protected IApexService waitForApexService() {
455             // Since apexd is a trusted platform component, synchronized calls are allowable
456             return IApexService.Stub.asInterface(
457                     Binder.allowBlocking(ServiceManager.waitForService("apexservice")));
458         }
459 
460         @Override
getAllApexInfos()461         ApexInfo[] getAllApexInfos() {
462             try {
463                 return waitForApexService().getAllPackages();
464             } catch (RemoteException re) {
465                 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
466                 throw new RuntimeException(re);
467             }
468         }
469 
470         @Override
notifyScanResult(List<ScanResult> scanResults)471         void notifyScanResult(List<ScanResult> scanResults) {
472             synchronized (mLock) {
473                 notifyScanResultLocked(scanResults);
474             }
475         }
476 
477         @GuardedBy("mLock")
notifyScanResultLocked(List<ScanResult> scanResults)478         private void notifyScanResultLocked(List<ScanResult> scanResults) {
479             mPackageNameToApexModuleName = new ArrayMap<>();
480             mApexModuleNameToActivePackageName = new ArrayMap<>();
481             for (ScanResult scanResult : scanResults) {
482                 ApexInfo ai = scanResult.apexInfo;
483                 String packageName = scanResult.packageName;
484                 for (ParsedApexSystemService service :
485                         scanResult.pkg.getApexSystemServices()) {
486                     String minSdkVersion = service.getMinSdkVersion();
487                     if (minSdkVersion != null && !UnboundedSdkLevel.isAtLeast(minSdkVersion)) {
488                         Slog.d(TAG, String.format(
489                                 "ApexSystemService %s with min_sdk_version=%s is skipped",
490                                 service.getName(), service.getMinSdkVersion()));
491                         continue;
492                     }
493                     String maxSdkVersion = service.getMaxSdkVersion();
494                     if (maxSdkVersion != null && !UnboundedSdkLevel.isAtMost(maxSdkVersion)) {
495                         Slog.d(TAG, String.format(
496                                 "ApexSystemService %s with max_sdk_version=%s is skipped",
497                                 service.getName(), service.getMaxSdkVersion()));
498                         continue;
499                     }
500 
501                     if (ai.isActive) {
502                         String name = service.getName();
503                         for (int j = 0; j < mApexSystemServices.size(); j++) {
504                             ApexSystemServiceInfo info = mApexSystemServices.get(j);
505                             if (info.getName().equals(name)) {
506                                 throw new IllegalStateException(TextUtils.formatSimple(
507                                         "Duplicate apex-system-service %s from %s, %s", name,
508                                         info.mJarPath, service.getJarPath()));
509                             }
510                         }
511                         ApexSystemServiceInfo info = new ApexSystemServiceInfo(
512                                 service.getName(), service.getJarPath(),
513                                 service.getInitOrder());
514                         mApexSystemServices.add(info);
515                     }
516                 }
517                 Collections.sort(mApexSystemServices);
518                 mPackageNameToApexModuleName.put(packageName, ai.moduleName);
519                 if (ai.isActive) {
520                     if (mApexModuleNameToActivePackageName.containsKey(ai.moduleName)) {
521                         throw new IllegalStateException(
522                                 "Two active packages have the same APEX module name: "
523                                         + ai.moduleName);
524                     }
525                     mApexModuleNameToActivePackageName.put(
526                             ai.moduleName, packageName);
527                 }
528             }
529         }
530 
531         @Override
getActiveApexInfos()532         public List<ActiveApexInfo> getActiveApexInfos() {
533             final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
534                     Trace.TRACE_TAG_PACKAGE_MANAGER);
535             synchronized (mLock) {
536                 if (mActiveApexInfosCache == null) {
537                     t.traceBegin("getActiveApexInfos_noCache");
538                     try {
539                         mActiveApexInfosCache = new ArraySet<>();
540                         final ApexInfo[] activePackages = waitForApexService().getActivePackages();
541                         for (int i = 0; i < activePackages.length; i++) {
542                             ApexInfo apexInfo = activePackages[i];
543                             mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
544                         }
545                     } catch (RemoteException e) {
546                         Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
547                     }
548                     t.traceEnd();
549                 }
550                 if (mActiveApexInfosCache != null) {
551                     return new ArrayList<>(mActiveApexInfosCache);
552                 } else {
553                     return Collections.emptyList();
554                 }
555             }
556         }
557 
558         @Override
559         @Nullable
getActiveApexPackageNameContainingPackage(String containedPackageName)560         public String getActiveApexPackageNameContainingPackage(String containedPackageName) {
561             Objects.requireNonNull(containedPackageName);
562             synchronized (mLock) {
563                 Preconditions.checkState(mPackageNameToApexModuleName != null,
564                         "APEX packages have not been scanned");
565                 int numApksInApex = mApksInApex.size();
566                 for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) {
567                     if (mApksInApex.valueAt(apkInApexNum).contains(containedPackageName)) {
568                         String apexModuleName = mApksInApex.keyAt(apkInApexNum);
569 
570                         int numApexPkgs = mPackageNameToApexModuleName.size();
571                         for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) {
572                             if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals(
573                                     apexModuleName)) {
574                                 return mPackageNameToApexModuleName.keyAt(apexPkgNum);
575                             }
576                         }
577                     }
578                 }
579             }
580 
581             return null;
582         }
583 
584         @Override
getStagedSessionInfo(int sessionId)585         @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
586             try {
587                 ApexSessionInfo apexSessionInfo =
588                         waitForApexService().getStagedSessionInfo(sessionId);
589                 if (apexSessionInfo.isUnknown) {
590                     return null;
591                 }
592                 return apexSessionInfo;
593             } catch (RemoteException re) {
594                 Slog.e(TAG, "Unable to contact apexservice", re);
595                 throw new RuntimeException(re);
596             }
597         }
598 
599         @Override
getSessions()600         SparseArray<ApexSessionInfo> getSessions() {
601             try {
602                 final ApexSessionInfo[] sessions = waitForApexService().getSessions();
603                 final SparseArray<ApexSessionInfo> result = new SparseArray<>(sessions.length);
604                 for (int i = 0; i < sessions.length; i++) {
605                     result.put(sessions[i].sessionId, sessions[i]);
606                 }
607                 return result;
608             } catch (RemoteException re) {
609                 Slog.e(TAG, "Unable to contact apexservice", re);
610                 throw new RuntimeException(re);
611             }
612         }
613 
614         @Override
submitStagedSession(ApexSessionParams params)615         ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
616             try {
617                 final ApexInfoList apexInfoList = new ApexInfoList();
618                 waitForApexService().submitStagedSession(params, apexInfoList);
619                 return apexInfoList;
620             } catch (RemoteException re) {
621                 Slog.e(TAG, "Unable to contact apexservice", re);
622                 throw new RuntimeException(re);
623             } catch (Exception e) {
624                 throw new PackageManagerException(
625                         PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
626                         "apexd verification failed : " + e.getMessage());
627             }
628         }
629 
630         @Override
getStagedApexInfos(ApexSessionParams params)631         ApexInfo[] getStagedApexInfos(ApexSessionParams params) {
632             try {
633                 return waitForApexService().getStagedApexInfos(params);
634             } catch (RemoteException re) {
635                 Slog.w(TAG, "Unable to contact apexservice" + re.getMessage());
636                 throw new RuntimeException(re);
637             } catch (Exception e) {
638                 Slog.w(TAG, "Failed to collect staged apex infos" + e.getMessage());
639                 return new ApexInfo[0];
640             }
641         }
642 
643         @Override
markStagedSessionReady(int sessionId)644         void markStagedSessionReady(int sessionId) throws PackageManagerException {
645             try {
646                 waitForApexService().markStagedSessionReady(sessionId);
647             } catch (RemoteException re) {
648                 Slog.e(TAG, "Unable to contact apexservice", re);
649                 throw new RuntimeException(re);
650             } catch (Exception e) {
651                 throw new PackageManagerException(
652                         PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
653                         "Failed to mark apexd session as ready : " + e.getMessage());
654             }
655         }
656 
657         @Override
markStagedSessionSuccessful(int sessionId)658         void markStagedSessionSuccessful(int sessionId) {
659             try {
660                 waitForApexService().markStagedSessionSuccessful(sessionId);
661             } catch (RemoteException re) {
662                 Slog.e(TAG, "Unable to contact apexservice", re);
663                 throw new RuntimeException(re);
664             } catch (Exception e) {
665                 // It is fine to just log an exception in this case. APEXd will be able to recover
666                 // in case markStagedSessionSuccessful fails.
667                 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
668             }
669         }
670 
671         @Override
isApexSupported()672         boolean isApexSupported() {
673             return true;
674         }
675 
676         @Override
revertActiveSessions()677         boolean revertActiveSessions() {
678             try {
679                 waitForApexService().revertActiveSessions();
680                 return true;
681             } catch (RemoteException re) {
682                 Slog.e(TAG, "Unable to contact apexservice", re);
683                 return false;
684             } catch (Exception e) {
685                 Slog.e(TAG, e.getMessage(), e);
686                 return false;
687             }
688         }
689 
690         @Override
abortStagedSession(int sessionId)691         boolean abortStagedSession(int sessionId) {
692             try {
693                 waitForApexService().abortStagedSession(sessionId);
694                 return true;
695             } catch (Exception e) {
696                 Slog.e(TAG, e.getMessage(), e);
697                 return false;
698             }
699         }
700 
701         @Override
uninstallApex(String apexPackagePath)702         boolean uninstallApex(String apexPackagePath) {
703             try {
704                 waitForApexService().unstagePackages(Collections.singletonList(apexPackagePath));
705                 return true;
706             } catch (Exception e) {
707                 return false;
708             }
709         }
710 
711         @Override
registerApkInApex(AndroidPackage pkg)712         void registerApkInApex(AndroidPackage pkg) {
713             synchronized (mLock) {
714                 for (ActiveApexInfo aai : mActiveApexInfosCache) {
715                     if (pkg.getBaseApkPath().startsWith(
716                             aai.apexDirectory.getAbsolutePath() + File.separator)) {
717                         List<String> apks = mApksInApex.get(aai.apexModuleName);
718                         if (apks == null) {
719                             apks = Lists.newArrayList();
720                             mApksInApex.put(aai.apexModuleName, apks);
721                         }
722                         Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of "
723                                 + aai.apexModuleName);
724                         apks.add(pkg.getPackageName());
725                     }
726                 }
727             }
728         }
729 
730         @Override
reportErrorWithApkInApex(String scanDirPath, String errorMsg)731         void reportErrorWithApkInApex(String scanDirPath, String errorMsg) {
732             synchronized (mLock) {
733                 for (ActiveApexInfo aai : mActiveApexInfosCache) {
734                     if (scanDirPath.startsWith(aai.apexDirectory.getAbsolutePath())) {
735                         mErrorWithApkInApex.put(aai.apexModuleName, errorMsg);
736                     }
737                 }
738             }
739         }
740 
741         @Override
742         @Nullable
getApkInApexInstallError(String apexPackageName)743         String getApkInApexInstallError(String apexPackageName) {
744             synchronized (mLock) {
745                 Preconditions.checkState(mPackageNameToApexModuleName != null,
746                         "APEX packages have not been scanned");
747                 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
748                 if (moduleName == null) {
749                     return null;
750                 }
751                 return mErrorWithApkInApex.get(moduleName);
752             }
753         }
754 
755         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
756         @Override
getApksInApex(String apexPackageName)757         public List<String> getApksInApex(String apexPackageName) {
758             synchronized (mLock) {
759                 Preconditions.checkState(mPackageNameToApexModuleName != null,
760                         "APEX packages have not been scanned");
761                 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
762                 if (moduleName == null) {
763                     return Collections.emptyList();
764                 }
765                 return mApksInApex.getOrDefault(moduleName, Collections.emptyList());
766             }
767         }
768 
769         @Override
770         @Nullable
getApexModuleNameForPackageName(String apexPackageName)771         public String getApexModuleNameForPackageName(String apexPackageName) {
772             synchronized (mLock) {
773                 Preconditions.checkState(mPackageNameToApexModuleName != null,
774                         "APEX packages have not been scanned");
775                 return mPackageNameToApexModuleName.get(apexPackageName);
776             }
777         }
778 
779         @Override
780         @Nullable
getActivePackageNameForApexModuleName(String apexModuleName)781         public String getActivePackageNameForApexModuleName(String apexModuleName) {
782             synchronized (mLock) {
783                 Preconditions.checkState(mApexModuleNameToActivePackageName != null,
784                         "APEX packages have not been scanned");
785                 return mApexModuleNameToActivePackageName.get(apexModuleName);
786             }
787         }
788 
789         @Override
snapshotCeData(int userId, int rollbackId, String apexPackageName)790         public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) {
791             String apexModuleName;
792             synchronized (mLock) {
793                 Preconditions.checkState(mPackageNameToApexModuleName != null,
794                         "APEX packages have not been scanned");
795                 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
796             }
797             if (apexModuleName == null) {
798                 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
799                 return false;
800             }
801             try {
802                 waitForApexService().snapshotCeData(userId, rollbackId, apexModuleName);
803                 return true;
804             } catch (Exception e) {
805                 Slog.e(TAG, e.getMessage(), e);
806                 return false;
807             }
808         }
809 
810         @Override
restoreCeData(int userId, int rollbackId, String apexPackageName)811         public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
812             String apexModuleName;
813             synchronized (mLock) {
814                 Preconditions.checkState(mPackageNameToApexModuleName != null,
815                         "APEX packages have not been scanned");
816                 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
817             }
818             if (apexModuleName == null) {
819                 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
820                 return false;
821             }
822             try {
823                 waitForApexService().restoreCeData(userId, rollbackId, apexModuleName);
824                 return true;
825             } catch (Exception e) {
826                 Slog.e(TAG, e.getMessage(), e);
827                 return false;
828             }
829         }
830 
831         @Override
destroyDeSnapshots(int rollbackId)832         public boolean destroyDeSnapshots(int rollbackId) {
833             try {
834                 waitForApexService().destroyDeSnapshots(rollbackId);
835                 return true;
836             } catch (Exception e) {
837                 Slog.e(TAG, e.getMessage(), e);
838                 return false;
839             }
840         }
841 
842         @Override
destroyCeSnapshots(int userId, int rollbackId)843         public boolean destroyCeSnapshots(int userId, int rollbackId) {
844             try {
845                 waitForApexService().destroyCeSnapshots(userId, rollbackId);
846                 return true;
847             } catch (Exception e) {
848                 Slog.e(TAG, e.getMessage(), e);
849                 return false;
850             }
851         }
852 
853         @Override
destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds)854         public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) {
855             try {
856                 waitForApexService().destroyCeSnapshotsNotSpecified(userId, retainRollbackIds);
857                 return true;
858             } catch (Exception e) {
859                 Slog.e(TAG, e.getMessage(), e);
860                 return false;
861             }
862         }
863 
864         @Override
markBootCompleted()865         public void markBootCompleted() {
866             try {
867                 waitForApexService().markBootCompleted();
868             } catch (RemoteException re) {
869                 Slog.e(TAG, "Unable to contact apexservice", re);
870             }
871         }
872 
873         @Override
calculateSizeForCompressedApex(CompressedApexInfoList infoList)874         public long calculateSizeForCompressedApex(CompressedApexInfoList infoList)
875                 throws RemoteException {
876             return waitForApexService().calculateSizeForCompressedApex(infoList);
877         }
878 
879         @Override
reserveSpaceForCompressedApex(CompressedApexInfoList infoList)880         public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList)
881                 throws RemoteException {
882             waitForApexService().reserveSpaceForCompressedApex(infoList);
883         }
884 
getSigningDetails(PackageInfo pkg)885         private SigningDetails getSigningDetails(PackageInfo pkg) throws PackageManagerException {
886             final int minSignatureScheme =
887                     ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
888                             pkg.applicationInfo.targetSdkVersion);
889             final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
890             final ParseResult<SigningDetails> result = ApkSignatureVerifier.verify(
891                     input, pkg.applicationInfo.sourceDir, minSignatureScheme);
892             if (result.isError()) {
893                 throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
894                         result.getException());
895             }
896             return result.getResult();
897         }
898 
checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)899         private void checkApexSignature(PackageInfo existingApexPkg, PackageInfo newApexPkg)
900                 throws PackageManagerException {
901             final SigningDetails existingSigningDetails = getSigningDetails(existingApexPkg);
902             final SigningDetails newSigningDetails = getSigningDetails(newApexPkg);
903             if (!newSigningDetails.checkCapability(existingSigningDetails,
904                       SigningDetails.CertCapabilities.INSTALLED_DATA)) {
905                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
906                           "APK container signature of " + newApexPkg.applicationInfo.sourceDir
907                                    + " is not compatible with currently installed on device");
908             }
909         }
910 
911         @Override
installPackage(File apexFile, boolean force)912         ApexInfo installPackage(File apexFile, boolean force)
913                 throws PackageManagerException {
914             try {
915                 return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath(),
916                         force);
917             } catch (RemoteException e) {
918                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
919                         "apexservice not available");
920             } catch (Exception e) {
921                 // TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here?
922                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
923                         e.getMessage());
924             }
925         }
926 
927         @Override
getApexSystemServices()928         public List<ApexSystemServiceInfo> getApexSystemServices() {
929             synchronized (mLock) {
930                 Preconditions.checkState(mApexSystemServices != null,
931                         "APEX packages have not been scanned");
932                 return mApexSystemServices;
933             }
934         }
935 
936         @Override
getBackingApexFile(File file)937         public File getBackingApexFile(File file) {
938             Path path = file.toPath();
939             if (!path.startsWith(Environment.getApexDirectory().toPath())) {
940                 return null;
941             }
942             if (path.getNameCount() < 2) {
943                 return null;
944             }
945             String moduleName = file.toPath().getName(1).toString();
946             final List<ActiveApexInfo> apexes = getActiveApexInfos();
947             for (int i = 0; i < apexes.size(); i++) {
948                 if (apexes.get(i).apexModuleName.equals(moduleName)) {
949                     return apexes.get(i).apexFile;
950                 }
951             }
952             return null;
953         }
954 
955         @Override
dump(PrintWriter pw)956         void dump(PrintWriter pw) {
957             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
958             try {
959                 ipw.println();
960                 ipw.println("APEX session state:");
961                 ipw.increaseIndent();
962                 final ApexSessionInfo[] sessions = waitForApexService().getSessions();
963                 for (ApexSessionInfo si : sessions) {
964                     ipw.println("Session ID: " + si.sessionId);
965                     ipw.increaseIndent();
966                     if (si.isUnknown) {
967                         ipw.println("State: UNKNOWN");
968                     } else if (si.isVerified) {
969                         ipw.println("State: VERIFIED");
970                     } else if (si.isStaged) {
971                         ipw.println("State: STAGED");
972                     } else if (si.isActivated) {
973                         ipw.println("State: ACTIVATED");
974                     } else if (si.isActivationFailed) {
975                         ipw.println("State: ACTIVATION FAILED");
976                     } else if (si.isSuccess) {
977                         ipw.println("State: SUCCESS");
978                     } else if (si.isRevertInProgress) {
979                         ipw.println("State: REVERT IN PROGRESS");
980                     } else if (si.isReverted) {
981                         ipw.println("State: REVERTED");
982                     } else if (si.isRevertFailed) {
983                         ipw.println("State: REVERT FAILED");
984                     }
985                     ipw.decreaseIndent();
986                 }
987                 ipw.decreaseIndent();
988                 ipw.println();
989             } catch (RemoteException e) {
990                 ipw.println("Couldn't communicate with apexd.");
991             }
992         }
993     }
994 }
995