blob: 90afefffdf2e45c644fe681214f64343ffd9a6c9 [file] [log] [blame]
Dario Freni2e8dffc2019-02-06 14:55:16 +00001/*
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
Nikita Ioffe278af8f2019-07-04 20:48:25 +010014 * limitations under the License.
Dario Freni2e8dffc2019-02-06 14:55:16 +000015 */
16
17package com.android.server.pm;
18
Gavin Corkeryef441722019-05-09 17:02:10 +010019import android.annotation.IntDef;
Dario Freni2e8dffc2019-02-06 14:55:16 +000020import android.annotation.Nullable;
21import android.apex.ApexInfo;
22import android.apex.ApexInfoList;
23import android.apex.ApexSessionInfo;
Oli Lanc72b0bb2019-12-02 14:03:55 +000024import android.apex.ApexSessionParams;
Dario Freni2e8dffc2019-02-06 14:55:16 +000025import android.apex.IApexService;
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000026import android.content.BroadcastReceiver;
Dario Freni14f885b2019-02-25 12:48:47 +000027import android.content.Context;
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000028import android.content.Intent;
29import android.content.IntentFilter;
Gavin Corkeryef441722019-05-09 17:02:10 +010030import android.content.pm.ApplicationInfo;
Dario Freni2e8dffc2019-02-06 14:55:16 +000031import android.content.pm.PackageInfo;
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +010032import android.content.pm.PackageInstaller;
Mohammad Samiul Islam7aa7d2e2019-03-27 12:23:47 +000033import android.content.pm.PackageManager;
Dario Freni2e8dffc2019-02-06 14:55:16 +000034import android.content.pm.PackageParser;
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +000035import android.content.pm.parsing.AndroidPackage;
Dario Frenic3e1bb722019-10-09 15:43:38 +010036import android.os.Environment;
Dario Freni2e8dffc2019-02-06 14:55:16 +000037import android.os.RemoteException;
38import android.os.ServiceManager;
Dario Freni2ce84342019-04-26 13:06:22 +010039import android.sysprop.ApexProperties;
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +000040import android.util.ArrayMap;
41import android.util.ArraySet;
Oli Lanbe7a42362019-11-28 11:34:21 +000042import android.util.Singleton;
Dario Freni2e8dffc2019-02-06 14:55:16 +000043import android.util.Slog;
44
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000045import com.android.internal.annotations.GuardedBy;
Bill Lin57f19302019-10-09 18:35:20 +080046import com.android.internal.annotations.VisibleForTesting;
Mohammad Samiul Islamb29dcce2019-08-16 10:10:07 +010047import com.android.internal.os.BackgroundThread;
Dario Freni2e8dffc2019-02-06 14:55:16 +000048import com.android.internal.util.IndentingPrintWriter;
49
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +000050import com.google.android.collect.Lists;
51
Dario Freni2e8dffc2019-02-06 14:55:16 +000052import java.io.File;
53import java.io.PrintWriter;
Gavin Corkeryef441722019-05-09 17:02:10 +010054import java.lang.annotation.Retention;
55import java.lang.annotation.RetentionPolicy;
56import java.util.ArrayList;
Nikita Ioffef012a222019-03-05 22:37:55 +000057import java.util.Collections;
Gavin Corkeryef441722019-05-09 17:02:10 +010058import java.util.HashSet;
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +000059import java.util.Iterator;
Gavin Corkeryef441722019-05-09 17:02:10 +010060import java.util.List;
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +000061import java.util.Map;
62import java.util.Set;
Gavin Corkeryef441722019-05-09 17:02:10 +010063import java.util.stream.Collectors;
Dario Freni2e8dffc2019-02-06 14:55:16 +000064
65/**
66 * ApexManager class handles communications with the apex service to perform operation and queries,
67 * as well as providing caching to avoid unnecessary calls to the service.
68 */
Oli Lan8ebc0432020-01-10 19:33:13 +000069public abstract class ApexManager {
Gavin Corkeryef441722019-05-09 17:02:10 +010070
Nikita Ioffe278af8f2019-07-04 20:48:25 +010071 private static final String TAG = "ApexManager";
Dario Freni14f885b2019-02-25 12:48:47 +000072
Gavin Corkeryef441722019-05-09 17:02:10 +010073 static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
74 static final int MATCH_FACTORY_PACKAGE = 1 << 1;
Gavin Corkeryef441722019-05-09 17:02:10 +010075
Oli Lanbe7a42362019-11-28 11:34:21 +000076 private static final Singleton<ApexManager> sApexManagerSingleton =
77 new Singleton<ApexManager>() {
78 @Override
79 protected ApexManager create() {
80 if (ApexProperties.updatable().orElse(false)) {
81 try {
82 return new ApexManagerImpl(IApexService.Stub.asInterface(
83 ServiceManager.getServiceOrThrow("apexservice")));
84 } catch (ServiceManager.ServiceNotFoundException e) {
85 throw new IllegalStateException(
86 "Required service apexservice not available");
87 }
88 } else {
89 return new ApexManagerFlattenedApex();
90 }
91 }
92 };
93
Nikita Ioffe278af8f2019-07-04 20:48:25 +010094 /**
Dario Frenic3e1bb722019-10-09 15:43:38 +010095 * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
96 * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
97 * evaluates to {@code true}.
Nikita Ioffe278af8f2019-07-04 20:48:25 +010098 */
Oli Lan7bbd8b42020-01-14 10:11:42 +000099 public static ApexManager getInstance() {
Oli Lanbe7a42362019-11-28 11:34:21 +0000100 return sApexManagerSingleton.get();
Dario Freni2e8dffc2019-02-06 14:55:16 +0000101 }
102
Dario Frenic3e1bb722019-10-09 15:43:38 +0100103 /**
104 * Minimal information about APEX mount points and the original APEX package they refer to.
105 */
106 static class ActiveApexInfo {
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000107 @Nullable public final String apexModuleName;
Dario Frenic3e1bb722019-10-09 15:43:38 +0100108 public final File apexDirectory;
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000109 public final File preInstalledApexPath;
Dario Frenic3e1bb722019-10-09 15:43:38 +0100110
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000111 private ActiveApexInfo(File apexDirectory, File preInstalledApexPath) {
112 this(null, apexDirectory, preInstalledApexPath);
113 }
114
115 private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory,
116 File preInstalledApexPath) {
117 this.apexModuleName = apexModuleName;
Dario Frenic3e1bb722019-10-09 15:43:38 +0100118 this.apexDirectory = apexDirectory;
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000119 this.preInstalledApexPath = preInstalledApexPath;
120 }
121
122 private ActiveApexInfo(ApexInfo apexInfo) {
123 this(
124 apexInfo.moduleName,
125 new File(Environment.getApexDirectory() + File.separator
126 + apexInfo.moduleName),
127 new File(apexInfo.preinstalledModulePath));
Dario Frenic3e1bb722019-10-09 15:43:38 +0100128 }
129 }
130
131 /**
132 * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
133 */
134 abstract List<ActiveApexInfo> getActiveApexInfos();
135
Oli Lanbe7a42362019-11-28 11:34:21 +0000136 abstract void systemReady(Context context);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100137
Dario Freni2e8dffc2019-02-06 14:55:16 +0000138 /**
Gavin Corkeryef441722019-05-09 17:02:10 +0100139 * Retrieves information about an APEX package.
Dario Freni2e8dffc2019-02-06 14:55:16 +0000140 *
141 * @param packageName the package name to look for. Note that this is the package name reported
142 * in the APK container manifest (i.e. AndroidManifest.xml), which might
143 * differ from the one reported in the APEX manifest (i.e.
144 * apex_manifest.json).
Gavin Corkeryef441722019-05-09 17:02:10 +0100145 * @param flags the type of package to return. This may match to active packages
146 * and factory (pre-installed) packages.
Dario Freni2e8dffc2019-02-06 14:55:16 +0000147 * @return a PackageInfo object with the information about the package, or null if the package
148 * is not found.
149 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100150 @Nullable
151 abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000152
153 /**
154 * Retrieves information about all active APEX packages.
155 *
Gavin Corkeryef441722019-05-09 17:02:10 +0100156 * @return a List of PackageInfo object, each one containing information about a different
Dario Freni2e8dffc2019-02-06 14:55:16 +0000157 * active package.
158 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100159 abstract List<PackageInfo> getActivePackages();
Gavin Corkeryef441722019-05-09 17:02:10 +0100160
161 /**
162 * Retrieves information about all active pre-installed APEX packages.
163 *
164 * @return a List of PackageInfo object, each one containing information about a different
165 * active pre-installed package.
166 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100167 abstract List<PackageInfo> getFactoryPackages();
Gavin Corkeryef441722019-05-09 17:02:10 +0100168
169 /**
170 * Retrieves information about all inactive APEX packages.
171 *
172 * @return a List of PackageInfo object, each one containing information about a different
173 * inactive package.
174 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100175 abstract List<PackageInfo> getInactivePackages();
Dario Freni2e8dffc2019-02-06 14:55:16 +0000176
177 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000178 * Checks if {@code packageName} is an apex package.
179 *
180 * @param packageName package to check.
181 * @return {@code true} if {@code packageName} is an apex package.
182 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100183 abstract boolean isApexPackage(String packageName);
Nikita Ioffef012a222019-03-05 22:37:55 +0000184
185 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000186 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
187 * track the different states of a session.
188 *
189 * @param sessionId the identifier of the session.
190 * @return an ApexSessionInfo object, or null if the session is not known.
191 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100192 @Nullable
193 abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000194
195 /**
196 * Submit a staged session to apex service. This causes the apex service to perform some initial
197 * verification and accept or reject the session. Submitting a session successfully is not
198 * enough for it to be activated at the next boot, the caller needs to call
199 * {@link #markStagedSessionReady(int)}.
200 *
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100201 * @throws PackageManagerException if call to apexd fails
Dario Freni2e8dffc2019-02-06 14:55:16 +0000202 */
Oli Lanc72b0bb2019-12-02 14:03:55 +0000203 abstract ApexInfoList submitStagedSession(ApexSessionParams params)
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100204 throws PackageManagerException;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000205
206 /**
Dario Frenia0e3dda2019-02-18 20:58:54 +0000207 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
Dario Freni2e8dffc2019-02-06 14:55:16 +0000208 * applied at next reboot.
209 *
210 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100211 * @throws PackageManagerException if call to apexd fails
Dario Freni2e8dffc2019-02-06 14:55:16 +0000212 */
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100213 abstract void markStagedSessionReady(int sessionId) throws PackageManagerException;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000214
215 /**
Nikita Ioffea820bd92019-02-15 14:22:44 +0000216 * Marks a staged session as successful.
217 *
218 * <p>Only activated session can be marked as successful.
219 *
220 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
221 * successful.
222 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100223 abstract void markStagedSessionSuccessful(int sessionId);
Nikita Ioffea820bd92019-02-15 14:22:44 +0000224
225 /**
Dario Freni83620602019-02-18 14:30:57 +0000226 * Whether the current device supports the management of APEX packages.
227 *
228 * @return true if APEX packages can be managed on this device, false otherwise.
229 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100230 abstract boolean isApexSupported();
Dario Freni83620602019-02-18 14:30:57 +0000231
232 /**
shafik07205e32019-02-07 20:12:33 +0000233 * Abandons the (only) active session previously submitted.
234 *
235 * @return {@code true} upon success, {@code false} if any remote exception occurs
236 */
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000237 abstract boolean revertActiveSessions();
shafik07205e32019-02-07 20:12:33 +0000238
239 /**
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000240 * Abandons the staged session with the given sessionId.
241 *
242 * @return {@code true} upon success, {@code false} if any remote exception occurs
243 */
244 abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
245
246 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000247 * Uninstalls given {@code apexPackage}.
248 *
249 * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
250 *
251 * @param apexPackagePath package to uninstall.
252 * @return {@code true} upon successful uninstall, {@code false} otherwise.
253 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100254 abstract boolean uninstallApex(String apexPackagePath);
Gavin Corkeryef441722019-05-09 17:02:10 +0100255
256 /**
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000257 * Registers an APK package as an embedded apk of apex.
258 */
259 abstract void registerApkInApex(AndroidPackage pkg);
260
261 /**
262 * Returns list of {@code packageName} of apks inside the given apex.
263 * @param apexPackageName Package name of the apk container of apex
264 */
265 abstract List<String> getApksInApex(String apexPackageName);
266
267 /**
Oli Lan8ebc0432020-01-10 19:33:13 +0000268 * Returns the apex module name for the given package name, if the package is an APEX. Otherwise
269 * returns {@code null}.
270 */
271 @Nullable
272 public abstract String getApexModuleNameForPackageName(String apexPackageName);
273
274 /**
Oli Lan7bbd8b42020-01-14 10:11:42 +0000275 * Copies the CE apex data directory for the given {@code userId} to a backup location, for use
276 * in case of rollback.
277 *
278 * @return long inode for the snapshot directory if the snapshot was successful, or -1 if not
279 */
280 public abstract long snapshotCeData(int userId, int rollbackId, String apexPackageName);
281
282 /**
283 * Restores the snapshot of the CE apex data directory for the given {@code userId}.
284 *
285 * @return boolean true if the restore was successful
286 */
287 public abstract boolean restoreCeData(int userId, int rollbackId, String apexPackageName);
288
289 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000290 * Dumps various state information to the provided {@link PrintWriter} object.
291 *
292 * @param pw the {@link PrintWriter} object to send information to.
293 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
294 * information about that specific package will be dumped.
295 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100296 abstract void dump(PrintWriter pw, @Nullable String packageName);
297
298 @IntDef(
299 flag = true,
300 prefix = { "MATCH_"},
301 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
302 @Retention(RetentionPolicy.SOURCE)
303 @interface PackageInfoFlags{}
304
305 /**
306 * An implementation of {@link ApexManager} that should be used in case device supports updating
307 * APEX packages.
308 */
Bill Lin57f19302019-10-09 18:35:20 +0800309 @VisibleForTesting
310 static class ApexManagerImpl extends ApexManager {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100311 private final IApexService mApexService;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100312 private final Object mLock = new Object();
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000313
314 @GuardedBy("mLock")
315 private Set<ActiveApexInfo> mActiveApexInfosCache;
316
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100317 /**
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000318 * Contains the list of {@code packageName}s of apks-in-apex for given
319 * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
320 * difference between {@code packageName} and {@code apexModuleName}.
321 */
322 @GuardedBy("mLock")
323 private Map<String, List<String>> mApksInApex = new ArrayMap<>();
324
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100325 @GuardedBy("mLock")
326 private List<PackageInfo> mAllPackagesCache;
327
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000328 /**
329 * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
330 * apk container has a reference name, called {@code packageName}, which is found inside the
331 * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference
332 * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file.
333 *
334 * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of
335 * the apk container to {@code apexModuleName} of the apex-payload inside.
336 */
337 @GuardedBy("mLock")
338 private Map<String, String> mPackageNameToApexModuleName;
339
Oli Lanbe7a42362019-11-28 11:34:21 +0000340 ApexManagerImpl(IApexService apexService) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100341 mApexService = apexService;
342 }
343
344 /**
345 * Whether an APEX package is active or not.
346 *
347 * @param packageInfo the package to check
348 * @return {@code true} if this package is active, {@code false} otherwise.
349 */
350 private static boolean isActive(PackageInfo packageInfo) {
351 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
352 }
353
354 /**
355 * Whether the APEX package is pre-installed or not.
356 *
357 * @param packageInfo the package to check
358 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
359 */
360 private static boolean isFactory(PackageInfo packageInfo) {
361 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
362 }
363
364 @Override
Dario Frenic3e1bb722019-10-09 15:43:38 +0100365 List<ActiveApexInfo> getActiveApexInfos() {
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000366 synchronized (mLock) {
367 if (mActiveApexInfosCache == null) {
368 try {
369 mActiveApexInfosCache = new ArraySet<>();
370 final ApexInfo[] activePackages = mApexService.getActivePackages();
371 for (int i = 0; i < activePackages.length; i++) {
372 ApexInfo apexInfo = activePackages[i];
373 mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
374 }
375 } catch (RemoteException e) {
376 Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
377 }
378 }
379 if (mActiveApexInfosCache != null) {
380 return new ArrayList<>(mActiveApexInfosCache);
381 } else {
382 return Collections.emptyList();
383 }
Dario Frenic3e1bb722019-10-09 15:43:38 +0100384 }
Dario Frenic3e1bb722019-10-09 15:43:38 +0100385 }
386
387 @Override
Oli Lanbe7a42362019-11-28 11:34:21 +0000388 void systemReady(Context context) {
389 context.registerReceiver(new BroadcastReceiver() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100390 @Override
391 public void onReceive(Context context, Intent intent) {
Nikita Ioffe8d325352019-08-23 18:26:31 +0100392 // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
393 // expensive to run it in broadcast handler thread.
394 BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
Oli Lanbe7a42362019-11-28 11:34:21 +0000395 context.unregisterReceiver(this);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000396 }
Nikita Ioffe8d325352019-08-23 18:26:31 +0100397 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100398 }
399
Mohammad Samiul Islam364e69b2020-01-08 14:39:29 +0000400 private void populatePackageNameToApexModuleNameIfNeeded() {
401 synchronized (mLock) {
402 if (mPackageNameToApexModuleName != null) {
403 return;
404 }
405 try {
406 mPackageNameToApexModuleName = new ArrayMap<>();
407 final ApexInfo[] allPkgs = mApexService.getAllPackages();
408 for (int i = 0; i < allPkgs.length; i++) {
409 ApexInfo ai = allPkgs[i];
410 PackageParser.PackageLite pkgLite;
411 try {
412 File apexFile = new File(ai.modulePath);
413 pkgLite = PackageParser.parsePackageLite(apexFile, 0);
414 } catch (PackageParser.PackageParserException pe) {
415 throw new IllegalStateException("Unable to parse: "
416 + ai.modulePath, pe);
417 }
418 mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName);
419 }
420 } catch (RemoteException re) {
421 Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re);
422 throw new RuntimeException(re);
423 }
424 }
425 }
426
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100427 private void populateAllPackagesCacheIfNeeded() {
428 synchronized (mLock) {
429 if (mAllPackagesCache != null) {
430 return;
431 }
432 try {
433 mAllPackagesCache = new ArrayList<>();
434 HashSet<String> activePackagesSet = new HashSet<>();
435 HashSet<String> factoryPackagesSet = new HashSet<>();
436 final ApexInfo[] allPkgs = mApexService.getAllPackages();
437 for (ApexInfo ai : allPkgs) {
438 // If the device is using flattened APEX, don't report any APEX
439 // packages since they won't be managed or updated by PackageManager.
440 if ((new File(ai.modulePath)).isDirectory()) {
441 break;
442 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100443 int flags = PackageManager.GET_META_DATA
444 | PackageManager.GET_SIGNING_CERTIFICATES
445 | PackageManager.GET_SIGNATURES;
446 PackageParser.Package pkg;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100447 try {
Oli Lanc2c7a222019-07-31 15:27:22 +0100448 File apexFile = new File(ai.modulePath);
449 PackageParser pp = new PackageParser();
450 pkg = pp.parsePackage(apexFile, flags, false);
451 PackageParser.collectCertificates(pkg, false);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100452 } catch (PackageParser.PackageParserException pe) {
453 throw new IllegalStateException("Unable to parse: " + ai, pe);
454 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100455
456 final PackageInfo packageInfo =
457 PackageParser.generatePackageInfo(pkg, ai, flags);
458 mAllPackagesCache.add(packageInfo);
459 if (ai.isActive) {
460 if (activePackagesSet.contains(packageInfo.packageName)) {
461 throw new IllegalStateException(
462 "Two active packages have the same name: "
463 + packageInfo.packageName);
464 }
465 activePackagesSet.add(packageInfo.packageName);
466 }
467 if (ai.isFactory) {
468 if (factoryPackagesSet.contains(packageInfo.packageName)) {
469 throw new IllegalStateException(
470 "Two factory packages have the same name: "
471 + packageInfo.packageName);
472 }
473 factoryPackagesSet.add(packageInfo.packageName);
474 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100475 }
476 } catch (RemoteException re) {
477 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
478 throw new RuntimeException(re);
479 }
480 }
481 }
482
483 @Override
484 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
485 populateAllPackagesCacheIfNeeded();
486 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
487 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
488 for (PackageInfo packageInfo: mAllPackagesCache) {
489 if (!packageInfo.packageName.equals(packageName)) {
490 continue;
491 }
Bill Lin4a352432019-10-09 16:22:25 +0800492 if ((matchActive && isActive(packageInfo))
493 || (matchFactory && isFactory(packageInfo))) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100494 return packageInfo;
495 }
496 }
497 return null;
498 }
499
500 @Override
501 List<PackageInfo> getActivePackages() {
502 populateAllPackagesCacheIfNeeded();
503 return mAllPackagesCache
504 .stream()
505 .filter(item -> isActive(item))
506 .collect(Collectors.toList());
507 }
508
509 @Override
510 List<PackageInfo> getFactoryPackages() {
511 populateAllPackagesCacheIfNeeded();
512 return mAllPackagesCache
513 .stream()
514 .filter(item -> isFactory(item))
515 .collect(Collectors.toList());
516 }
517
518 @Override
519 List<PackageInfo> getInactivePackages() {
520 populateAllPackagesCacheIfNeeded();
521 return mAllPackagesCache
522 .stream()
523 .filter(item -> !isActive(item))
524 .collect(Collectors.toList());
525 }
526
527 @Override
528 boolean isApexPackage(String packageName) {
529 if (!isApexSupported()) return false;
530 populateAllPackagesCacheIfNeeded();
531 for (PackageInfo packageInfo : mAllPackagesCache) {
532 if (packageInfo.packageName.equals(packageName)) {
533 return true;
534 }
535 }
536 return false;
537 }
538
539 @Override
540 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
541 try {
542 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
543 if (apexSessionInfo.isUnknown) {
544 return null;
545 }
546 return apexSessionInfo;
547 } catch (RemoteException re) {
548 Slog.e(TAG, "Unable to contact apexservice", re);
549 throw new RuntimeException(re);
550 }
551 }
552
553 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000554 ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100555 try {
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100556 final ApexInfoList apexInfoList = new ApexInfoList();
Oli Lanc72b0bb2019-12-02 14:03:55 +0000557 mApexService.submitStagedSession(params, apexInfoList);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100558 return apexInfoList;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100559 } catch (RemoteException re) {
560 Slog.e(TAG, "Unable to contact apexservice", re);
561 throw new RuntimeException(re);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100562 } catch (Exception e) {
563 throw new PackageManagerException(
564 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
565 "apexd verification failed : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100566 }
567 }
568
569 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100570 void markStagedSessionReady(int sessionId) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100571 try {
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100572 mApexService.markStagedSessionReady(sessionId);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100573 } catch (RemoteException re) {
574 Slog.e(TAG, "Unable to contact apexservice", re);
575 throw new RuntimeException(re);
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100576 } catch (Exception e) {
577 throw new PackageManagerException(
578 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
579 "Failed to mark apexd session as ready : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100580 }
581 }
582
583 @Override
584 void markStagedSessionSuccessful(int sessionId) {
585 try {
586 mApexService.markStagedSessionSuccessful(sessionId);
587 } catch (RemoteException re) {
588 Slog.e(TAG, "Unable to contact apexservice", re);
589 throw new RuntimeException(re);
590 } catch (Exception e) {
591 // It is fine to just log an exception in this case. APEXd will be able to recover
592 // in case markStagedSessionSuccessful fails.
593 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
594 }
595 }
596
597 @Override
598 boolean isApexSupported() {
599 return true;
600 }
601
602 @Override
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000603 boolean revertActiveSessions() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100604 try {
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000605 mApexService.revertActiveSessions();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100606 return true;
607 } catch (RemoteException re) {
608 Slog.e(TAG, "Unable to contact apexservice", re);
609 return false;
Mohammad Samiul Islam7e9fdb02019-11-28 18:14:40 +0000610 } catch (Exception e) {
611 Slog.e(TAG, e.getMessage(), e);
612 return false;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100613 }
614 }
615
616 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000617 boolean abortStagedSession(int sessionId) throws PackageManagerException {
618 try {
619 mApexService.abortStagedSession(sessionId);
620 return true;
621 } catch (RemoteException re) {
622 Slog.e(TAG, "Unable to contact apexservice", re);
623 return false;
624 } catch (Exception e) {
625 throw new PackageManagerException(
626 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
627 "Failed to abort staged session : " + e.getMessage());
628 }
629 }
630
631 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100632 boolean uninstallApex(String apexPackagePath) {
633 try {
634 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
635 return true;
636 } catch (Exception e) {
637 return false;
638 }
639 }
640
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000641 @Override
642 void registerApkInApex(AndroidPackage pkg) {
643 synchronized (mLock) {
644 final Iterator<ActiveApexInfo> it = mActiveApexInfosCache.iterator();
645 while (it.hasNext()) {
646 final ActiveApexInfo aai = it.next();
647 if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) {
648 List<String> apks = mApksInApex.get(aai.apexModuleName);
649 if (apks == null) {
650 apks = Lists.newArrayList();
651 mApksInApex.put(aai.apexModuleName, apks);
652 }
653 apks.add(pkg.getPackageName());
654 }
655 }
656 }
657 }
658
659 @Override
660 List<String> getApksInApex(String apexPackageName) {
Mohammad Samiul Islam364e69b2020-01-08 14:39:29 +0000661 populatePackageNameToApexModuleNameIfNeeded();
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000662 synchronized (mLock) {
663 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
664 if (moduleName == null) {
665 return Collections.emptyList();
666 }
667 return mApksInApex.getOrDefault(moduleName, Collections.emptyList());
668 }
669 }
670
Oli Lan8ebc0432020-01-10 19:33:13 +0000671 @Override
672 @Nullable
673 public String getApexModuleNameForPackageName(String apexPackageName) {
674 populatePackageNameToApexModuleNameIfNeeded();
675 synchronized (mLock) {
676 return mPackageNameToApexModuleName.get(apexPackageName);
677 }
678 }
679
Oli Lan7bbd8b42020-01-14 10:11:42 +0000680 @Override
681 public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
682 populatePackageNameToApexModuleNameIfNeeded();
683 String apexModuleName;
684 synchronized (mLock) {
685 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
686 }
687 if (apexModuleName == null) {
688 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
689 return -1;
690 }
691 try {
692 return mApexService.snapshotCeData(userId, rollbackId, apexModuleName);
693 } catch (Exception e) {
694 Slog.e(TAG, e.getMessage(), e);
695 return -1;
696 }
697 }
698
699 @Override
700 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
701 populatePackageNameToApexModuleNameIfNeeded();
702 String apexModuleName;
703 synchronized (mLock) {
704 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
705 }
706 if (apexModuleName == null) {
707 Slog.e(TAG, "Invalid apex package name: " + apexPackageName);
708 return false;
709 }
710 try {
711 mApexService.restoreCeData(userId, rollbackId, apexModuleName);
712 return true;
713 } catch (Exception e) {
714 Slog.e(TAG, e.getMessage(), e);
715 return false;
716 }
717 }
718
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100719 /**
720 * Dump information about the packages contained in a particular cache
721 * @param packagesCache the cache to print information about.
722 * @param packageName a {@link String} containing a package name, or {@code null}. If set,
723 * only information about that specific package will be dumped.
724 * @param ipw the {@link IndentingPrintWriter} object to send information to.
725 */
726 void dumpFromPackagesCache(
727 List<PackageInfo> packagesCache,
728 @Nullable String packageName,
729 IndentingPrintWriter ipw) {
730 ipw.println();
731 ipw.increaseIndent();
732 for (PackageInfo pi : packagesCache) {
733 if (packageName != null && !packageName.equals(pi.packageName)) {
734 continue;
735 }
736 ipw.println(pi.packageName);
737 ipw.increaseIndent();
738 ipw.println("Version: " + pi.versionCode);
739 ipw.println("Path: " + pi.applicationInfo.sourceDir);
740 ipw.println("IsActive: " + isActive(pi));
741 ipw.println("IsFactory: " + isFactory(pi));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000742 ipw.decreaseIndent();
743 }
744 ipw.decreaseIndent();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100745 ipw.println();
746 }
747
748 @Override
749 void dump(PrintWriter pw, @Nullable String packageName) {
750 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
751 try {
752 populateAllPackagesCacheIfNeeded();
753 ipw.println();
754 ipw.println("Active APEX packages:");
755 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
756 ipw.println("Inactive APEX packages:");
757 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
758 ipw.println("Factory APEX packages:");
759 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
760 ipw.increaseIndent();
761 ipw.println("APEX session state:");
762 ipw.increaseIndent();
763 final ApexSessionInfo[] sessions = mApexService.getSessions();
764 for (ApexSessionInfo si : sessions) {
765 ipw.println("Session ID: " + si.sessionId);
766 ipw.increaseIndent();
767 if (si.isUnknown) {
768 ipw.println("State: UNKNOWN");
769 } else if (si.isVerified) {
770 ipw.println("State: VERIFIED");
771 } else if (si.isStaged) {
772 ipw.println("State: STAGED");
773 } else if (si.isActivated) {
774 ipw.println("State: ACTIVATED");
775 } else if (si.isActivationFailed) {
776 ipw.println("State: ACTIVATION FAILED");
777 } else if (si.isSuccess) {
778 ipw.println("State: SUCCESS");
Mohammad Samiul Islam2dfc86a2019-11-20 13:53:07 +0000779 } else if (si.isRevertInProgress) {
780 ipw.println("State: REVERT IN PROGRESS");
781 } else if (si.isReverted) {
782 ipw.println("State: REVERTED");
783 } else if (si.isRevertFailed) {
784 ipw.println("State: REVERT FAILED");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100785 }
786 ipw.decreaseIndent();
787 }
788 ipw.decreaseIndent();
789 } catch (RemoteException e) {
790 ipw.println("Couldn't communicate with apexd.");
791 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000792 }
793 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000794
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100795 /**
796 * An implementation of {@link ApexManager} that should be used in case device does not support
797 * updating APEX packages.
798 */
Dario Frenic3e1bb722019-10-09 15:43:38 +0100799 private static final class ApexManagerFlattenedApex extends ApexManager {
Dario Frenic3e1bb722019-10-09 15:43:38 +0100800 @Override
801 List<ActiveApexInfo> getActiveApexInfos() {
802 // There is no apexd running in case of flattened apex
803 // We look up the /apex directory and identify the active APEX modules from there.
804 // As "preinstalled" path, we just report /system since in the case of flattened APEX
805 // the /apex directory is just a symlink to /system/apex.
806 List<ActiveApexInfo> result = new ArrayList<>();
807 File apexDir = Environment.getApexDirectory();
808 // In flattened configuration, init special-case the art directory and bind-mounts
809 // com.android.art.{release|debug} to com.android.art. At the time of writing, these
810 // directories are copied from the kArtApexDirNames variable in
811 // system/core/init/mount_namespace.cpp.
812 String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
813 if (apexDir.isDirectory()) {
814 File[] files = apexDir.listFiles();
815 // listFiles might be null if system server doesn't have permission to read
816 // a directory.
817 if (files != null) {
818 for (File file : files) {
819 if (file.isDirectory() && !file.getName().contains("@")) {
820 for (String skipDir : skipDirs) {
821 if (file.getName().equals(skipDir)) {
822 continue;
823 }
824 }
825 result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
826 }
827 }
828 }
829 }
830 return result;
831 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100832
833 @Override
Oli Lanbe7a42362019-11-28 11:34:21 +0000834 void systemReady(Context context) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100835 // No-op
836 }
837
838 @Override
839 PackageInfo getPackageInfo(String packageName, int flags) {
840 return null;
841 }
842
843 @Override
844 List<PackageInfo> getActivePackages() {
845 return Collections.emptyList();
846 }
847
848 @Override
849 List<PackageInfo> getFactoryPackages() {
850 return Collections.emptyList();
851 }
852
853 @Override
854 List<PackageInfo> getInactivePackages() {
855 return Collections.emptyList();
856 }
857
858 @Override
859 boolean isApexPackage(String packageName) {
860 return false;
861 }
862
863 @Override
864 ApexSessionInfo getStagedSessionInfo(int sessionId) {
865 throw new UnsupportedOperationException();
866 }
867
868 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000869 ApexInfoList submitStagedSession(ApexSessionParams params)
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100870 throws PackageManagerException {
871 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
872 "Device doesn't support updating APEX");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100873 }
874
875 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100876 void markStagedSessionReady(int sessionId) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100877 throw new UnsupportedOperationException();
878 }
879
880 @Override
881 void markStagedSessionSuccessful(int sessionId) {
882 throw new UnsupportedOperationException();
883 }
884
885 @Override
886 boolean isApexSupported() {
887 return false;
888 }
889
890 @Override
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000891 boolean revertActiveSessions() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100892 throw new UnsupportedOperationException();
893 }
894
895 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000896 boolean abortStagedSession(int sessionId) throws PackageManagerException {
897 throw new UnsupportedOperationException();
898 }
899
900 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100901 boolean uninstallApex(String apexPackagePath) {
902 throw new UnsupportedOperationException();
903 }
904
905 @Override
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000906 void registerApkInApex(AndroidPackage pkg) {
907 // No-op
908 }
909
910 @Override
911 List<String> getApksInApex(String apexPackageName) {
912 return Collections.emptyList();
913 }
914
915 @Override
Oli Lan8ebc0432020-01-10 19:33:13 +0000916 @Nullable
917 public String getApexModuleNameForPackageName(String apexPackageName) {
918 return null;
919 }
920
921 @Override
Oli Lan7bbd8b42020-01-14 10:11:42 +0000922 public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
923 throw new UnsupportedOperationException();
924 }
925
926 @Override
927 public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
928 throw new UnsupportedOperationException();
929 }
930
931 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100932 void dump(PrintWriter pw, String packageName) {
933 // No-op
934 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000935 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900936}