blob: 28079099469a27e7e91ca3c58a63985d08e9fbc3 [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 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +010069abstract 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 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000268 * Dumps various state information to the provided {@link PrintWriter} object.
269 *
270 * @param pw the {@link PrintWriter} object to send information to.
271 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
272 * information about that specific package will be dumped.
273 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100274 abstract void dump(PrintWriter pw, @Nullable String packageName);
275
276 @IntDef(
277 flag = true,
278 prefix = { "MATCH_"},
279 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
280 @Retention(RetentionPolicy.SOURCE)
281 @interface PackageInfoFlags{}
282
283 /**
284 * An implementation of {@link ApexManager} that should be used in case device supports updating
285 * APEX packages.
286 */
Bill Lin57f19302019-10-09 18:35:20 +0800287 @VisibleForTesting
288 static class ApexManagerImpl extends ApexManager {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100289 private final IApexService mApexService;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100290 private final Object mLock = new Object();
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000291
292 @GuardedBy("mLock")
293 private Set<ActiveApexInfo> mActiveApexInfosCache;
294
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100295 /**
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000296 * Contains the list of {@code packageName}s of apks-in-apex for given
297 * {@code apexModuleName}. See {@link #mPackageNameToApexModuleName} to understand the
298 * difference between {@code packageName} and {@code apexModuleName}.
299 */
300 @GuardedBy("mLock")
301 private Map<String, List<String>> mApksInApex = new ArrayMap<>();
302
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100303 @GuardedBy("mLock")
304 private List<PackageInfo> mAllPackagesCache;
305
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000306 /**
307 * An APEX is a file format that delivers the apex-payload wrapped in an apk container. The
308 * apk container has a reference name, called {@code packageName}, which is found inside the
309 * {@code AndroidManifest.xml}. The apex payload inside the container also has a reference
310 * name, called {@code apexModuleName}, which is found in {@code apex_manifest.json} file.
311 *
312 * {@link #mPackageNameToApexModuleName} contains the mapping from {@code packageName} of
313 * the apk container to {@code apexModuleName} of the apex-payload inside.
314 */
315 @GuardedBy("mLock")
316 private Map<String, String> mPackageNameToApexModuleName;
317
Oli Lanbe7a42362019-11-28 11:34:21 +0000318 ApexManagerImpl(IApexService apexService) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100319 mApexService = apexService;
320 }
321
322 /**
323 * Whether an APEX package is active or not.
324 *
325 * @param packageInfo the package to check
326 * @return {@code true} if this package is active, {@code false} otherwise.
327 */
328 private static boolean isActive(PackageInfo packageInfo) {
329 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
330 }
331
332 /**
333 * Whether the APEX package is pre-installed or not.
334 *
335 * @param packageInfo the package to check
336 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
337 */
338 private static boolean isFactory(PackageInfo packageInfo) {
339 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
340 }
341
342 @Override
Dario Frenic3e1bb722019-10-09 15:43:38 +0100343 List<ActiveApexInfo> getActiveApexInfos() {
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000344 synchronized (mLock) {
345 if (mActiveApexInfosCache == null) {
346 try {
347 mActiveApexInfosCache = new ArraySet<>();
348 final ApexInfo[] activePackages = mApexService.getActivePackages();
349 for (int i = 0; i < activePackages.length; i++) {
350 ApexInfo apexInfo = activePackages[i];
351 mActiveApexInfosCache.add(new ActiveApexInfo(apexInfo));
352 }
353 } catch (RemoteException e) {
354 Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
355 }
356 }
357 if (mActiveApexInfosCache != null) {
358 return new ArrayList<>(mActiveApexInfosCache);
359 } else {
360 return Collections.emptyList();
361 }
Dario Frenic3e1bb722019-10-09 15:43:38 +0100362 }
Dario Frenic3e1bb722019-10-09 15:43:38 +0100363 }
364
365 @Override
Oli Lanbe7a42362019-11-28 11:34:21 +0000366 void systemReady(Context context) {
367 context.registerReceiver(new BroadcastReceiver() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100368 @Override
369 public void onReceive(Context context, Intent intent) {
Nikita Ioffe8d325352019-08-23 18:26:31 +0100370 // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
371 // expensive to run it in broadcast handler thread.
372 BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
Oli Lanbe7a42362019-11-28 11:34:21 +0000373 context.unregisterReceiver(this);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000374 }
Nikita Ioffe8d325352019-08-23 18:26:31 +0100375 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100376 }
377
Mohammad Samiul Islam364e69b2020-01-08 14:39:29 +0000378 private void populatePackageNameToApexModuleNameIfNeeded() {
379 synchronized (mLock) {
380 if (mPackageNameToApexModuleName != null) {
381 return;
382 }
383 try {
384 mPackageNameToApexModuleName = new ArrayMap<>();
385 final ApexInfo[] allPkgs = mApexService.getAllPackages();
386 for (int i = 0; i < allPkgs.length; i++) {
387 ApexInfo ai = allPkgs[i];
388 PackageParser.PackageLite pkgLite;
389 try {
390 File apexFile = new File(ai.modulePath);
391 pkgLite = PackageParser.parsePackageLite(apexFile, 0);
392 } catch (PackageParser.PackageParserException pe) {
393 throw new IllegalStateException("Unable to parse: "
394 + ai.modulePath, pe);
395 }
396 mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName);
397 }
398 } catch (RemoteException re) {
399 Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re);
400 throw new RuntimeException(re);
401 }
402 }
403 }
404
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100405 private void populateAllPackagesCacheIfNeeded() {
406 synchronized (mLock) {
407 if (mAllPackagesCache != null) {
408 return;
409 }
410 try {
411 mAllPackagesCache = new ArrayList<>();
412 HashSet<String> activePackagesSet = new HashSet<>();
413 HashSet<String> factoryPackagesSet = new HashSet<>();
414 final ApexInfo[] allPkgs = mApexService.getAllPackages();
415 for (ApexInfo ai : allPkgs) {
416 // If the device is using flattened APEX, don't report any APEX
417 // packages since they won't be managed or updated by PackageManager.
418 if ((new File(ai.modulePath)).isDirectory()) {
419 break;
420 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100421 int flags = PackageManager.GET_META_DATA
422 | PackageManager.GET_SIGNING_CERTIFICATES
423 | PackageManager.GET_SIGNATURES;
424 PackageParser.Package pkg;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100425 try {
Oli Lanc2c7a222019-07-31 15:27:22 +0100426 File apexFile = new File(ai.modulePath);
427 PackageParser pp = new PackageParser();
428 pkg = pp.parsePackage(apexFile, flags, false);
429 PackageParser.collectCertificates(pkg, false);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100430 } catch (PackageParser.PackageParserException pe) {
431 throw new IllegalStateException("Unable to parse: " + ai, pe);
432 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100433
434 final PackageInfo packageInfo =
435 PackageParser.generatePackageInfo(pkg, ai, flags);
436 mAllPackagesCache.add(packageInfo);
437 if (ai.isActive) {
438 if (activePackagesSet.contains(packageInfo.packageName)) {
439 throw new IllegalStateException(
440 "Two active packages have the same name: "
441 + packageInfo.packageName);
442 }
443 activePackagesSet.add(packageInfo.packageName);
444 }
445 if (ai.isFactory) {
446 if (factoryPackagesSet.contains(packageInfo.packageName)) {
447 throw new IllegalStateException(
448 "Two factory packages have the same name: "
449 + packageInfo.packageName);
450 }
451 factoryPackagesSet.add(packageInfo.packageName);
452 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100453 }
454 } catch (RemoteException re) {
455 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
456 throw new RuntimeException(re);
457 }
458 }
459 }
460
461 @Override
462 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
463 populateAllPackagesCacheIfNeeded();
464 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
465 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
466 for (PackageInfo packageInfo: mAllPackagesCache) {
467 if (!packageInfo.packageName.equals(packageName)) {
468 continue;
469 }
Bill Lin4a352432019-10-09 16:22:25 +0800470 if ((matchActive && isActive(packageInfo))
471 || (matchFactory && isFactory(packageInfo))) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100472 return packageInfo;
473 }
474 }
475 return null;
476 }
477
478 @Override
479 List<PackageInfo> getActivePackages() {
480 populateAllPackagesCacheIfNeeded();
481 return mAllPackagesCache
482 .stream()
483 .filter(item -> isActive(item))
484 .collect(Collectors.toList());
485 }
486
487 @Override
488 List<PackageInfo> getFactoryPackages() {
489 populateAllPackagesCacheIfNeeded();
490 return mAllPackagesCache
491 .stream()
492 .filter(item -> isFactory(item))
493 .collect(Collectors.toList());
494 }
495
496 @Override
497 List<PackageInfo> getInactivePackages() {
498 populateAllPackagesCacheIfNeeded();
499 return mAllPackagesCache
500 .stream()
501 .filter(item -> !isActive(item))
502 .collect(Collectors.toList());
503 }
504
505 @Override
506 boolean isApexPackage(String packageName) {
507 if (!isApexSupported()) return false;
508 populateAllPackagesCacheIfNeeded();
509 for (PackageInfo packageInfo : mAllPackagesCache) {
510 if (packageInfo.packageName.equals(packageName)) {
511 return true;
512 }
513 }
514 return false;
515 }
516
517 @Override
518 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
519 try {
520 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
521 if (apexSessionInfo.isUnknown) {
522 return null;
523 }
524 return apexSessionInfo;
525 } catch (RemoteException re) {
526 Slog.e(TAG, "Unable to contact apexservice", re);
527 throw new RuntimeException(re);
528 }
529 }
530
531 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000532 ApexInfoList submitStagedSession(ApexSessionParams params) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100533 try {
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100534 final ApexInfoList apexInfoList = new ApexInfoList();
Oli Lanc72b0bb2019-12-02 14:03:55 +0000535 mApexService.submitStagedSession(params, apexInfoList);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100536 return apexInfoList;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100537 } catch (RemoteException re) {
538 Slog.e(TAG, "Unable to contact apexservice", re);
539 throw new RuntimeException(re);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100540 } catch (Exception e) {
541 throw new PackageManagerException(
542 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
543 "apexd verification failed : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100544 }
545 }
546
547 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100548 void markStagedSessionReady(int sessionId) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100549 try {
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100550 mApexService.markStagedSessionReady(sessionId);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100551 } catch (RemoteException re) {
552 Slog.e(TAG, "Unable to contact apexservice", re);
553 throw new RuntimeException(re);
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100554 } catch (Exception e) {
555 throw new PackageManagerException(
556 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
557 "Failed to mark apexd session as ready : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100558 }
559 }
560
561 @Override
562 void markStagedSessionSuccessful(int sessionId) {
563 try {
564 mApexService.markStagedSessionSuccessful(sessionId);
565 } catch (RemoteException re) {
566 Slog.e(TAG, "Unable to contact apexservice", re);
567 throw new RuntimeException(re);
568 } catch (Exception e) {
569 // It is fine to just log an exception in this case. APEXd will be able to recover
570 // in case markStagedSessionSuccessful fails.
571 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
572 }
573 }
574
575 @Override
576 boolean isApexSupported() {
577 return true;
578 }
579
580 @Override
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000581 boolean revertActiveSessions() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100582 try {
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000583 mApexService.revertActiveSessions();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100584 return true;
585 } catch (RemoteException re) {
586 Slog.e(TAG, "Unable to contact apexservice", re);
587 return false;
Mohammad Samiul Islam7e9fdb02019-11-28 18:14:40 +0000588 } catch (Exception e) {
589 Slog.e(TAG, e.getMessage(), e);
590 return false;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100591 }
592 }
593
594 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000595 boolean abortStagedSession(int sessionId) throws PackageManagerException {
596 try {
597 mApexService.abortStagedSession(sessionId);
598 return true;
599 } catch (RemoteException re) {
600 Slog.e(TAG, "Unable to contact apexservice", re);
601 return false;
602 } catch (Exception e) {
603 throw new PackageManagerException(
604 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
605 "Failed to abort staged session : " + e.getMessage());
606 }
607 }
608
609 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100610 boolean uninstallApex(String apexPackagePath) {
611 try {
612 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
613 return true;
614 } catch (Exception e) {
615 return false;
616 }
617 }
618
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000619 @Override
620 void registerApkInApex(AndroidPackage pkg) {
621 synchronized (mLock) {
622 final Iterator<ActiveApexInfo> it = mActiveApexInfosCache.iterator();
623 while (it.hasNext()) {
624 final ActiveApexInfo aai = it.next();
625 if (pkg.getBaseCodePath().startsWith(aai.apexDirectory.getAbsolutePath())) {
626 List<String> apks = mApksInApex.get(aai.apexModuleName);
627 if (apks == null) {
628 apks = Lists.newArrayList();
629 mApksInApex.put(aai.apexModuleName, apks);
630 }
631 apks.add(pkg.getPackageName());
632 }
633 }
634 }
635 }
636
637 @Override
638 List<String> getApksInApex(String apexPackageName) {
Mohammad Samiul Islam364e69b2020-01-08 14:39:29 +0000639 populatePackageNameToApexModuleNameIfNeeded();
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000640 synchronized (mLock) {
641 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
642 if (moduleName == null) {
643 return Collections.emptyList();
644 }
645 return mApksInApex.getOrDefault(moduleName, Collections.emptyList());
646 }
647 }
648
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100649 /**
650 * Dump information about the packages contained in a particular cache
651 * @param packagesCache the cache to print information about.
652 * @param packageName a {@link String} containing a package name, or {@code null}. If set,
653 * only information about that specific package will be dumped.
654 * @param ipw the {@link IndentingPrintWriter} object to send information to.
655 */
656 void dumpFromPackagesCache(
657 List<PackageInfo> packagesCache,
658 @Nullable String packageName,
659 IndentingPrintWriter ipw) {
660 ipw.println();
661 ipw.increaseIndent();
662 for (PackageInfo pi : packagesCache) {
663 if (packageName != null && !packageName.equals(pi.packageName)) {
664 continue;
665 }
666 ipw.println(pi.packageName);
667 ipw.increaseIndent();
668 ipw.println("Version: " + pi.versionCode);
669 ipw.println("Path: " + pi.applicationInfo.sourceDir);
670 ipw.println("IsActive: " + isActive(pi));
671 ipw.println("IsFactory: " + isFactory(pi));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000672 ipw.decreaseIndent();
673 }
674 ipw.decreaseIndent();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100675 ipw.println();
676 }
677
678 @Override
679 void dump(PrintWriter pw, @Nullable String packageName) {
680 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
681 try {
682 populateAllPackagesCacheIfNeeded();
683 ipw.println();
684 ipw.println("Active APEX packages:");
685 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
686 ipw.println("Inactive APEX packages:");
687 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
688 ipw.println("Factory APEX packages:");
689 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
690 ipw.increaseIndent();
691 ipw.println("APEX session state:");
692 ipw.increaseIndent();
693 final ApexSessionInfo[] sessions = mApexService.getSessions();
694 for (ApexSessionInfo si : sessions) {
695 ipw.println("Session ID: " + si.sessionId);
696 ipw.increaseIndent();
697 if (si.isUnknown) {
698 ipw.println("State: UNKNOWN");
699 } else if (si.isVerified) {
700 ipw.println("State: VERIFIED");
701 } else if (si.isStaged) {
702 ipw.println("State: STAGED");
703 } else if (si.isActivated) {
704 ipw.println("State: ACTIVATED");
705 } else if (si.isActivationFailed) {
706 ipw.println("State: ACTIVATION FAILED");
707 } else if (si.isSuccess) {
708 ipw.println("State: SUCCESS");
Mohammad Samiul Islam2dfc86a2019-11-20 13:53:07 +0000709 } else if (si.isRevertInProgress) {
710 ipw.println("State: REVERT IN PROGRESS");
711 } else if (si.isReverted) {
712 ipw.println("State: REVERTED");
713 } else if (si.isRevertFailed) {
714 ipw.println("State: REVERT FAILED");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100715 }
716 ipw.decreaseIndent();
717 }
718 ipw.decreaseIndent();
719 } catch (RemoteException e) {
720 ipw.println("Couldn't communicate with apexd.");
721 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000722 }
723 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000724
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100725 /**
726 * An implementation of {@link ApexManager} that should be used in case device does not support
727 * updating APEX packages.
728 */
Dario Frenic3e1bb722019-10-09 15:43:38 +0100729 private static final class ApexManagerFlattenedApex extends ApexManager {
Dario Frenic3e1bb722019-10-09 15:43:38 +0100730 @Override
731 List<ActiveApexInfo> getActiveApexInfos() {
732 // There is no apexd running in case of flattened apex
733 // We look up the /apex directory and identify the active APEX modules from there.
734 // As "preinstalled" path, we just report /system since in the case of flattened APEX
735 // the /apex directory is just a symlink to /system/apex.
736 List<ActiveApexInfo> result = new ArrayList<>();
737 File apexDir = Environment.getApexDirectory();
738 // In flattened configuration, init special-case the art directory and bind-mounts
739 // com.android.art.{release|debug} to com.android.art. At the time of writing, these
740 // directories are copied from the kArtApexDirNames variable in
741 // system/core/init/mount_namespace.cpp.
742 String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
743 if (apexDir.isDirectory()) {
744 File[] files = apexDir.listFiles();
745 // listFiles might be null if system server doesn't have permission to read
746 // a directory.
747 if (files != null) {
748 for (File file : files) {
749 if (file.isDirectory() && !file.getName().contains("@")) {
750 for (String skipDir : skipDirs) {
751 if (file.getName().equals(skipDir)) {
752 continue;
753 }
754 }
755 result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
756 }
757 }
758 }
759 }
760 return result;
761 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100762
763 @Override
Oli Lanbe7a42362019-11-28 11:34:21 +0000764 void systemReady(Context context) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100765 // No-op
766 }
767
768 @Override
769 PackageInfo getPackageInfo(String packageName, int flags) {
770 return null;
771 }
772
773 @Override
774 List<PackageInfo> getActivePackages() {
775 return Collections.emptyList();
776 }
777
778 @Override
779 List<PackageInfo> getFactoryPackages() {
780 return Collections.emptyList();
781 }
782
783 @Override
784 List<PackageInfo> getInactivePackages() {
785 return Collections.emptyList();
786 }
787
788 @Override
789 boolean isApexPackage(String packageName) {
790 return false;
791 }
792
793 @Override
794 ApexSessionInfo getStagedSessionInfo(int sessionId) {
795 throw new UnsupportedOperationException();
796 }
797
798 @Override
Oli Lanc72b0bb2019-12-02 14:03:55 +0000799 ApexInfoList submitStagedSession(ApexSessionParams params)
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100800 throws PackageManagerException {
801 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
802 "Device doesn't support updating APEX");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100803 }
804
805 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100806 void markStagedSessionReady(int sessionId) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100807 throw new UnsupportedOperationException();
808 }
809
810 @Override
811 void markStagedSessionSuccessful(int sessionId) {
812 throw new UnsupportedOperationException();
813 }
814
815 @Override
816 boolean isApexSupported() {
817 return false;
818 }
819
820 @Override
Mohammad Samiul Islam1a96eb62019-11-21 10:38:06 +0000821 boolean revertActiveSessions() {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100822 throw new UnsupportedOperationException();
823 }
824
825 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000826 boolean abortStagedSession(int sessionId) throws PackageManagerException {
827 throw new UnsupportedOperationException();
828 }
829
830 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100831 boolean uninstallApex(String apexPackagePath) {
832 throw new UnsupportedOperationException();
833 }
834
835 @Override
Mohammad Samiul Islam3fcecfc2019-12-20 17:46:01 +0000836 void registerApkInApex(AndroidPackage pkg) {
837 // No-op
838 }
839
840 @Override
841 List<String> getApksInApex(String apexPackageName) {
842 return Collections.emptyList();
843 }
844
845 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100846 void dump(PrintWriter pw, String packageName) {
847 // No-op
848 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000849 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900850}