blob: c6d2b334bd7116024d83ec98abd0f462246ac2e4 [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 Lanbe7a42362019-11-28 11:34:21 +000099 static ApexManager getInstance() {
100 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 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000275 * Dumps various state information to the provided {@link PrintWriter} object.
276 *
277 * @param pw the {@link PrintWriter} object to send information to.
278 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
279 * information about that specific package will be dumped.
280 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100281 abstract void dump(PrintWriter pw, @Nullable String packageName);
282
283 @IntDef(
284 flag = true,
285 prefix = { "MATCH_"},
286 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
287 @Retention(RetentionPolicy.SOURCE)
288 @interface PackageInfoFlags{}
289
290 /**
291 * An implementation of {@link ApexManager} that should be used in case device supports updating
292 * APEX packages.
293 */
Bill Lin57f19302019-10-09 18:35:20 +0800294 @VisibleForTesting
295 static class ApexManagerImpl extends ApexManager {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100296 private final IApexService mApexService;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100297 private final Object mLock = new Object();
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000298
299 @GuardedBy("mLock")
300 private Set<ActiveApexInfo> mActiveApexInfosCache;
301
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100302 /**
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000303 * Contains the list of {@code packageName}s of apks-in-apex for given
304 * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
305 * difference between {@code packageName} and {@code apexModuleName}.
306 */
307 @GuardedBy("mLock")
308 private Map<String, List<String>> mApksInApex = new ArrayMap<>();
309
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100310 @GuardedBy("mLock")
311 private List<PackageInfo> mAllPackagesCache;
312
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000313 /**
314 * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
315 * apk container has a reference name, called {@code packageName}, which is found inside the
316 * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference
317 * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file.
318 *
319 * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of
320 * the apk container to {@code apexModuleName} of the apex-payload inside.
321 */
322 @GuardedBy("mLock")
323 private Map<String, String> mPackageNameToApexModuleName;
324
Oli Lanbe7a42362019-11-28 11:34:21 +0000325 ApexManagerImpl(IApexService apexService) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100326 mApexService = apexService;
327 }
328
329 /**
330 * Whether an APEX package is active or not.
331 *
332 * @param packageInfo the package to check
333 * @return {@code true} if this package is active, {@code false} otherwise.
334 */
335 private static boolean isActive(PackageInfo packageInfo) {
336 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
337 }
338
339 /**
340 * Whether the APEX package is pre-installed or not.
341 *
342 * @param packageInfo the package to check
343 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
344 */
345 private static boolean isFactory(PackageInfo packageInfo) {
346 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
347 }
348
349 @Override
Dario Frenic3e1bb722019-10-09 15:43:38 +0100350 List<ActiveApexInfo> getActiveApexInfos() {
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000351 synchronized (mLock) {
352 if (mActiveApexInfosCache == null) {
353 try {
354 mActiveApexInfosCache = new ArraySet<>();
355 final ApexInfo[] activePackages = mApexService.getActivePackages();
356 for (int i = 0; i < activePackages.length; i++) {
357 ApexInfo apexInfo = activePackages[i];
358 mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
359 }
360 } catch (RemoteException e) {
361 Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
362 }
363 }
364 if (mActiveApexInfosCache != null) {
365 return new ArrayList<>(mActiveApexInfosCache);
366 } else {
367 return Collections.emptyList();
368 }
Dario Frenic3e1bb722019-10-09 15:43:38 +0100369 }
Dario Frenic3e1bb722019-10-09 15:43:38 +0100370 }
371
372 @Override
Oli Lanbe7a42362019-11-28 11:34:21 +0000373 void systemReady(Context context) {
374 context.registerReceiver(new BroadcastReceiver() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100375 @Override
376 public void onReceive(Context context, Intent intent) {
Nikita Ioffe8d325352019-08-23 18:26:31 +0100377 // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
378 // expensive to run it in broadcast handler thread.
379 BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
Oli Lanbe7a42362019-11-28 11:34:21 +0000380 context.unregisterReceiver(this);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000381 }
Nikita Ioffe8d325352019-08-23 18:26:31 +0100382 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100383 }
384
Mohammad Samiul Islam364e69b2020-01-08 14:39:29 +0000385 private void populatePackageNameToApexModuleNameIfNeeded() {
386 synchronized (mLock) {
387 if (mPackageNameToApexModuleName != null) {
388 return;
389 }
390 try {
391 mPackageNameToApexModuleName = new ArrayMap<>();
392 final ApexInfo[] allPkgs = mApexService.getAllPackages();
393 for (int i = 0; i < allPkgs.length; i++) {
394 ApexInfo ai = allPkgs[i];
395 PackageParser.PackageLite pkgLite;
396 try {
397 File apexFile = new File(ai.modulePath);
398 pkgLite = PackageParser.parsePackageLite(apexFile, 0);
399 } catch (PackageParser.PackageParserException pe) {
400 throw new IllegalStateException("Unable to parse: "
401 + ai.modulePath, pe);
402 }
403 mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName);
404 }
405 } catch (RemoteException re) {
406 Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re);
407 throw new RuntimeException(re);
408 }
409 }
410 }
411
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100412 private void populateAllPackagesCacheIfNeeded() {
413 synchronized (mLock) {
414 if (mAllPackagesCache != null) {
415 return;
416 }
417 try {
418 mAllPackagesCache = new ArrayList<>();
419 HashSet<String> activePackagesSet = new HashSet<>();
420 HashSet<String> factoryPackagesSet = new HashSet<>();
421 final ApexInfo[] allPkgs = mApexService.getAllPackages();
422 for (ApexInfo ai : allPkgs) {
423 // If the device is using flattened APEX, don't report any APEX
424 // packages since they won't be managed or updated by PackageManager.
425 if ((new File(ai.modulePath)).isDirectory()) {
426 break;
427 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100428 int flags = PackageManager.GET_META_DATA
429 | PackageManager.GET_SIGNING_CERTIFICATES
430 | PackageManager.GET_SIGNATURES;
431 PackageParser.Package pkg;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100432 try {
Oli Lanc2c7a222019-07-31 15:27:22 +0100433 File apexFile = new File(ai.modulePath);
434 PackageParser pp = new PackageParser();
435 pkg = pp.parsePackage(apexFile, flags, false);
436 PackageParser.collectCertificates(pkg, false);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100437 } catch (PackageParser.PackageParserException pe) {
438 throw new IllegalStateException("Unable to parse: " + ai, pe);
439 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100440
441 final PackageInfo packageInfo =
442 PackageParser.generatePackageInfo(pkg, ai, flags);
443 mAllPackagesCache.add(packageInfo);
444 if (ai.isActive) {
445 if (activePackagesSet.contains(packageInfo.packageName)) {
446 throw new IllegalStateException(
447 "Two active packages have the same name: "
448 + packageInfo.packageName);
449 }
450 activePackagesSet.add(packageInfo.packageName);
451 }
452 if (ai.isFactory) {
453 if (factoryPackagesSet.contains(packageInfo.packageName)) {
454 throw new IllegalStateException(
455 "Two factory packages have the same name: "
456 + packageInfo.packageName);
457 }
458 factoryPackagesSet.add(packageInfo.packageName);
459 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100460 }
461 } catch (RemoteException re) {
462 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
463 throw new RuntimeException(re);
464 }
465 }
466 }
467
468 @Override
469 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
470 populateAllPackagesCacheIfNeeded();
471 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
472 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
473 for (PackageInfo packageInfo: mAllPackagesCache) {
474 if (!packageInfo.packageName.equals(packageName)) {
475 continue;
476 }
Bill Lin4a352432019-10-09 16:22:25 +0800477 if ((matchActive && isActive(packageInfo))
478 || (matchFactory && isFactory(packageInfo))) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100479 return packageInfo;
480 }
481 }
482 return null;
483 }
484
485 @Override
486 List<PackageInfo> getActivePackages() {
487 populateAllPackagesCacheIfNeeded();
488 return mAllPackagesCache
489 .stream()
490 .filter(item -> isActive(item))
491 .collect(Collectors.toList());
492 }
493
494 @Override
495 List<PackageInfo> getFactoryPackages() {
496 populateAllPackagesCacheIfNeeded();
497 return mAllPackagesCache
498 .stream()
499 .filter(item -> isFactory(item))
500 .collect(Collectors.toList());
501 }
502
503 @Override
504 List<PackageInfo> getInactivePackages() {
505 populateAllPackagesCacheIfNeeded();
506 return mAllPackagesCache
507 .stream()
508 .filter(item -> !isActive(item))
509 .collect(Collectors.toList());
510 }
511
512 @Override
513 boolean isApexPackage(String packageName) {
514 if (!isApexSupported()) return false;
515 populateAllPackagesCacheIfNeeded();
516 for (PackageInfo packageInfo : mAllPackagesCache) {
517 if (packageInfo.packageName.equals(packageName)) {
518 return true;
519 }
520 }
521 return false;
522 }
523
524 @Override
525 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
526 try {
527 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
528 if (apexSessionInfo.isUnknown) {
529 return null;
530 }
531 return apexSessionInfo;
532 } catch (RemoteException re) {
533 Slog.e(TAG, "Unable to contact apexservice", re);
534 throw new RuntimeException(re);
535 }
536 }
537
538 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000539 ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100540 try {
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100541 final ApexInfoList apexInfoList = new ApexInfoList();
Oli Lanc72b0bb2019-12-02 14:03:55 +0000542 mApexService.submitStagedSession(params, apexInfoList);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100543 return apexInfoList;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100544 } catch (RemoteException re) {
545 Slog.e(TAG, "Unable to contact apexservice", re);
546 throw new RuntimeException(re);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100547 } catch (Exception e) {
548 throw new PackageManagerException(
549 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
550 "apexd verification failed : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100551 }
552 }
553
554 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100555 void markStagedSessionReady(int sessionId) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100556 try {
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100557 mApexService.markStagedSessionReady(sessionId);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100558 } catch (RemoteException re) {
559 Slog.e(TAG, "Unable to contact apexservice", re);
560 throw new RuntimeException(re);
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100561 } catch (Exception e) {
562 throw new PackageManagerException(
563 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
564 "Failed to mark apexd session as ready : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100565 }
566 }
567
568 @Override
569 void markStagedSessionSuccessful(int sessionId) {
570 try {
571 mApexService.markStagedSessionSuccessful(sessionId);
572 } catch (RemoteException re) {
573 Slog.e(TAG, "Unable to contact apexservice", re);
574 throw new RuntimeException(re);
575 } catch (Exception e) {
576 // It is fine to just log an exception in this case. APEXd will be able to recover
577 // in case markStagedSessionSuccessful fails.
578 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
579 }
580 }
581
582 @Override
583 boolean isApexSupported() {
584 return true;
585 }
586
587 @Override
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000588 boolean revertActiveSessions() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100589 try {
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000590 mApexService.revertActiveSessions();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100591 return true;
592 } catch (RemoteException re) {
593 Slog.e(TAG, "Unable to contact apexservice", re);
594 return false;
Mohammad Samiul Islam7e9fdb02019-11-28 18:14:40 +0000595 } catch (Exception e) {
596 Slog.e(TAG, e.getMessage(), e);
597 return false;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100598 }
599 }
600
601 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000602 boolean abortStagedSession(int sessionId) throws PackageManagerException {
603 try {
604 mApexService.abortStagedSession(sessionId);
605 return true;
606 } catch (RemoteException re) {
607 Slog.e(TAG, "Unable to contact apexservice", re);
608 return false;
609 } catch (Exception e) {
610 throw new PackageManagerException(
611 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
612 "Failed to abort staged session : " + e.getMessage());
613 }
614 }
615
616 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100617 boolean uninstallApex(String apexPackagePath) {
618 try {
619 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
620 return true;
621 } catch (Exception e) {
622 return false;
623 }
624 }
625
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000626 @Override
627 void registerApkInApex(AndroidPackage pkg) {
628 synchronized (mLock) {
629 final Iterator<ActiveApexInfo> it = mActiveApexInfosCache.iterator();
630 while (it.hasNext()) {
631 final ActiveApexInfo aai = it.next();
632 if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) {
633 List<String> apks = mApksInApex.get(aai.apexModuleName);
634 if (apks == null) {
635 apks = Lists.newArrayList();
636 mApksInApex.put(aai.apexModuleName, apks);
637 }
638 apks.add(pkg.getPackageName());
639 }
640 }
641 }
642 }
643
644 @Override
645 List<String> getApksInApex(String apexPackageName) {
Mohammad Samiul Islam364e69b2020-01-08 14:39:29 +0000646 populatePackageNameToApexModuleNameIfNeeded();
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000647 synchronized (mLock) {
648 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
649 if (moduleName == null) {
650 return Collections.emptyList();
651 }
652 return mApksInApex.getOrDefault(moduleName, Collections.emptyList());
653 }
654 }
655
Oli Lan8ebc0432020-01-10 19:33:13 +0000656 @Override
657 @Nullable
658 public String getApexModuleNameForPackageName(String apexPackageName) {
659 populatePackageNameToApexModuleNameIfNeeded();
660 synchronized (mLock) {
661 return mPackageNameToApexModuleName.get(apexPackageName);
662 }
663 }
664
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100665 /**
666 * Dump information about the packages contained in a particular cache
667 * @param packagesCache the cache to print information about.
668 * @param packageName a {@link String} containing a package name, or {@code null}. If set,
669 * only information about that specific package will be dumped.
670 * @param ipw the {@link IndentingPrintWriter} object to send information to.
671 */
672 void dumpFromPackagesCache(
673 List<PackageInfo> packagesCache,
674 @Nullable String packageName,
675 IndentingPrintWriter ipw) {
676 ipw.println();
677 ipw.increaseIndent();
678 for (PackageInfo pi : packagesCache) {
679 if (packageName != null && !packageName.equals(pi.packageName)) {
680 continue;
681 }
682 ipw.println(pi.packageName);
683 ipw.increaseIndent();
684 ipw.println("Version: " + pi.versionCode);
685 ipw.println("Path: " + pi.applicationInfo.sourceDir);
686 ipw.println("IsActive: " + isActive(pi));
687 ipw.println("IsFactory: " + isFactory(pi));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000688 ipw.decreaseIndent();
689 }
690 ipw.decreaseIndent();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100691 ipw.println();
692 }
693
694 @Override
695 void dump(PrintWriter pw, @Nullable String packageName) {
696 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
697 try {
698 populateAllPackagesCacheIfNeeded();
699 ipw.println();
700 ipw.println("Active APEX packages:");
701 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
702 ipw.println("Inactive APEX packages:");
703 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
704 ipw.println("Factory APEX packages:");
705 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
706 ipw.increaseIndent();
707 ipw.println("APEX session state:");
708 ipw.increaseIndent();
709 final ApexSessionInfo[] sessions = mApexService.getSessions();
710 for (ApexSessionInfo si : sessions) {
711 ipw.println("Session ID: " + si.sessionId);
712 ipw.increaseIndent();
713 if (si.isUnknown) {
714 ipw.println("State: UNKNOWN");
715 } else if (si.isVerified) {
716 ipw.println("State: VERIFIED");
717 } else if (si.isStaged) {
718 ipw.println("State: STAGED");
719 } else if (si.isActivated) {
720 ipw.println("State: ACTIVATED");
721 } else if (si.isActivationFailed) {
722 ipw.println("State: ACTIVATION FAILED");
723 } else if (si.isSuccess) {
724 ipw.println("State: SUCCESS");
Mohammad Samiul Islam2dfc86a2019-11-20 13:53:07 +0000725 } else if (si.isRevertInProgress) {
726 ipw.println("State: REVERT IN PROGRESS");
727 } else if (si.isReverted) {
728 ipw.println("State: REVERTED");
729 } else if (si.isRevertFailed) {
730 ipw.println("State: REVERT FAILED");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100731 }
732 ipw.decreaseIndent();
733 }
734 ipw.decreaseIndent();
735 } catch (RemoteException e) {
736 ipw.println("Couldn't communicate with apexd.");
737 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000738 }
739 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000740
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100741 /**
742 * An implementation of {@link ApexManager} that should be used in case device does not support
743 * updating APEX packages.
744 */
Dario Frenic3e1bb722019-10-09 15:43:38 +0100745 private static final class ApexManagerFlattenedApex extends ApexManager {
Dario Frenic3e1bb722019-10-09 15:43:38 +0100746 @Override
747 List<ActiveApexInfo> getActiveApexInfos() {
748 // There is no apexd running in case of flattened apex
749 // We look up the /apex directory and identify the active APEX modules from there.
750 // As "preinstalled" path, we just report /system since in the case of flattened APEX
751 // the /apex directory is just a symlink to /system/apex.
752 List<ActiveApexInfo> result = new ArrayList<>();
753 File apexDir = Environment.getApexDirectory();
754 // In flattened configuration, init special-case the art directory and bind-mounts
755 // com.android.art.{release|debug} to com.android.art. At the time of writing, these
756 // directories are copied from the kArtApexDirNames variable in
757 // system/core/init/mount_namespace.cpp.
758 String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
759 if (apexDir.isDirectory()) {
760 File[] files = apexDir.listFiles();
761 // listFiles might be null if system server doesn't have permission to read
762 // a directory.
763 if (files != null) {
764 for (File file : files) {
765 if (file.isDirectory() && !file.getName().contains("@")) {
766 for (String skipDir : skipDirs) {
767 if (file.getName().equals(skipDir)) {
768 continue;
769 }
770 }
771 result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
772 }
773 }
774 }
775 }
776 return result;
777 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100778
779 @Override
Oli Lanbe7a42362019-11-28 11:34:21 +0000780 void systemReady(Context context) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100781 // No-op
782 }
783
784 @Override
785 PackageInfo getPackageInfo(String packageName, int flags) {
786 return null;
787 }
788
789 @Override
790 List<PackageInfo> getActivePackages() {
791 return Collections.emptyList();
792 }
793
794 @Override
795 List<PackageInfo> getFactoryPackages() {
796 return Collections.emptyList();
797 }
798
799 @Override
800 List<PackageInfo> getInactivePackages() {
801 return Collections.emptyList();
802 }
803
804 @Override
805 boolean isApexPackage(String packageName) {
806 return false;
807 }
808
809 @Override
810 ApexSessionInfo getStagedSessionInfo(int sessionId) {
811 throw new UnsupportedOperationException();
812 }
813
814 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000815 ApexInfoList submitStagedSession(ApexSessionParams params)
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100816 throws PackageManagerException {
817 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
818 "Device doesn't support updating APEX");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100819 }
820
821 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100822 void markStagedSessionReady(int sessionId) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100823 throw new UnsupportedOperationException();
824 }
825
826 @Override
827 void markStagedSessionSuccessful(int sessionId) {
828 throw new UnsupportedOperationException();
829 }
830
831 @Override
832 boolean isApexSupported() {
833 return false;
834 }
835
836 @Override
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000837 boolean revertActiveSessions() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100838 throw new UnsupportedOperationException();
839 }
840
841 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000842 boolean abortStagedSession(int sessionId) throws PackageManagerException {
843 throw new UnsupportedOperationException();
844 }
845
846 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100847 boolean uninstallApex(String apexPackagePath) {
848 throw new UnsupportedOperationException();
849 }
850
851 @Override
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000852 void registerApkInApex(AndroidPackage pkg) {
853 // No-op
854 }
855
856 @Override
857 List<String> getApksInApex(String apexPackageName) {
858 return Collections.emptyList();
859 }
860
861 @Override
Oli Lan8ebc0432020-01-10 19:33:13 +0000862 @Nullable
863 public String getApexModuleNameForPackageName(String apexPackageName) {
864 return null;
865 }
866
867 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100868 void dump(PrintWriter pw, String packageName) {
869 // No-op
870 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000871 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900872}