blob: 4f9b3962a9ea944fdeda69da363cb0a3e0f6ff81 [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.NonNull;
21import android.annotation.Nullable;
22import android.apex.ApexInfo;
23import android.apex.ApexInfoList;
24import android.apex.ApexSessionInfo;
25import 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;
Dario Frenic3e1bb722019-10-09 15:43:38 +010035import android.os.Environment;
Dario Freni2e8dffc2019-02-06 14:55:16 +000036import android.os.RemoteException;
37import android.os.ServiceManager;
Dario Freni2ce84342019-04-26 13:06:22 +010038import android.sysprop.ApexProperties;
Dario Freni2e8dffc2019-02-06 14:55:16 +000039import android.util.Slog;
40
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000041import com.android.internal.annotations.GuardedBy;
Bill Lin57f19302019-10-09 18:35:20 +080042import com.android.internal.annotations.VisibleForTesting;
Mohammad Samiul Islamb29dcce2019-08-16 10:10:07 +010043import com.android.internal.os.BackgroundThread;
Dario Freni2e8dffc2019-02-06 14:55:16 +000044import com.android.internal.util.IndentingPrintWriter;
45
46import java.io.File;
47import java.io.PrintWriter;
Gavin Corkeryef441722019-05-09 17:02:10 +010048import java.lang.annotation.Retention;
49import java.lang.annotation.RetentionPolicy;
50import java.util.ArrayList;
Dario Frenic3e1bb722019-10-09 15:43:38 +010051import java.util.Arrays;
Nikita Ioffef012a222019-03-05 22:37:55 +000052import java.util.Collections;
Gavin Corkeryef441722019-05-09 17:02:10 +010053import java.util.HashSet;
54import java.util.List;
55import java.util.stream.Collectors;
Dario Freni2e8dffc2019-02-06 14:55:16 +000056
57/**
58 * ApexManager class handles communications with the apex service to perform operation and queries,
59 * as well as providing caching to avoid unnecessary calls to the service.
60 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +010061abstract class ApexManager {
Gavin Corkeryef441722019-05-09 17:02:10 +010062
Nikita Ioffe278af8f2019-07-04 20:48:25 +010063 private static final String TAG = "ApexManager";
Dario Freni14f885b2019-02-25 12:48:47 +000064
Gavin Corkeryef441722019-05-09 17:02:10 +010065 static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
66 static final int MATCH_FACTORY_PACKAGE = 1 << 1;
Gavin Corkeryef441722019-05-09 17:02:10 +010067
Nikita Ioffe278af8f2019-07-04 20:48:25 +010068 /**
Dario Frenic3e1bb722019-10-09 15:43:38 +010069 * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
70 * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
71 * evaluates to {@code true}.
Nikita Ioffe278af8f2019-07-04 20:48:25 +010072 */
73 static ApexManager create(Context systemContext) {
74 if (ApexProperties.updatable().orElse(false)) {
Martijn Coenen3f629942019-02-22 13:15:46 +010075 try {
Nikita Ioffe278af8f2019-07-04 20:48:25 +010076 return new ApexManagerImpl(systemContext, IApexService.Stub.asInterface(
77 ServiceManager.getServiceOrThrow("apexservice")));
78 } catch (ServiceManager.ServiceNotFoundException e) {
79 throw new IllegalStateException("Required service apexservice not available");
Martijn Coenen3f629942019-02-22 13:15:46 +010080 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +010081 } else {
Dario Frenic3e1bb722019-10-09 15:43:38 +010082 return new ApexManagerFlattenedApex();
Dario Freni2e8dffc2019-02-06 14:55:16 +000083 }
84 }
85
Dario Frenic3e1bb722019-10-09 15:43:38 +010086 /**
87 * Minimal information about APEX mount points and the original APEX package they refer to.
88 */
89 static class ActiveApexInfo {
90 public final File apexDirectory;
91 public final File preinstalledApexPath;
92
93 private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) {
94 this.apexDirectory = apexDirectory;
95 this.preinstalledApexPath = preinstalledApexPath;
96 }
97 }
98
99 /**
100 * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
101 */
102 abstract List<ActiveApexInfo> getActiveApexInfos();
103
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100104 abstract void systemReady();
105
Dario Freni2e8dffc2019-02-06 14:55:16 +0000106 /**
Gavin Corkeryef441722019-05-09 17:02:10 +0100107 * Retrieves information about an APEX package.
Dario Freni2e8dffc2019-02-06 14:55:16 +0000108 *
109 * @param packageName the package name to look for. Note that this is the package name reported
110 * in the APK container manifest (i.e. AndroidManifest.xml), which might
111 * differ from the one reported in the APEX manifest (i.e.
112 * apex_manifest.json).
Gavin Corkeryef441722019-05-09 17:02:10 +0100113 * @param flags the type of package to return. This may match to active packages
114 * and factory (pre-installed) packages.
Dario Freni2e8dffc2019-02-06 14:55:16 +0000115 * @return a PackageInfo object with the information about the package, or null if the package
116 * is not found.
117 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100118 @Nullable
119 abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000120
121 /**
122 * Retrieves information about all active APEX packages.
123 *
Gavin Corkeryef441722019-05-09 17:02:10 +0100124 * @return a List of PackageInfo object, each one containing information about a different
Dario Freni2e8dffc2019-02-06 14:55:16 +0000125 * active package.
126 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100127 abstract List<PackageInfo> getActivePackages();
Gavin Corkeryef441722019-05-09 17:02:10 +0100128
129 /**
130 * Retrieves information about all active pre-installed APEX packages.
131 *
132 * @return a List of PackageInfo object, each one containing information about a different
133 * active pre-installed package.
134 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100135 abstract List<PackageInfo> getFactoryPackages();
Gavin Corkeryef441722019-05-09 17:02:10 +0100136
137 /**
138 * Retrieves information about all inactive APEX packages.
139 *
140 * @return a List of PackageInfo object, each one containing information about a different
141 * inactive package.
142 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100143 abstract List<PackageInfo> getInactivePackages();
Dario Freni2e8dffc2019-02-06 14:55:16 +0000144
145 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000146 * Checks if {@code packageName} is an apex package.
147 *
148 * @param packageName package to check.
149 * @return {@code true} if {@code packageName} is an apex package.
150 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100151 abstract boolean isApexPackage(String packageName);
Nikita Ioffef012a222019-03-05 22:37:55 +0000152
153 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000154 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
155 * track the different states of a session.
156 *
157 * @param sessionId the identifier of the session.
158 * @return an ApexSessionInfo object, or null if the session is not known.
159 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100160 @Nullable
161 abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000162
163 /**
164 * Submit a staged session to apex service. This causes the apex service to perform some initial
165 * verification and accept or reject the session. Submitting a session successfully is not
166 * enough for it to be activated at the next boot, the caller needs to call
167 * {@link #markStagedSessionReady(int)}.
168 *
169 * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted.
170 * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain
171 * an array of identifiers of all the child sessions. Otherwise it should
172 * be an empty array.
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100173 * @throws PackageManagerException if call to apexd fails
Dario Freni2e8dffc2019-02-06 14:55:16 +0000174 */
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100175 abstract ApexInfoList submitStagedSession(int sessionId, @NonNull int[] childSessionIds)
176 throws PackageManagerException;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000177
178 /**
Dario Frenia0e3dda2019-02-18 20:58:54 +0000179 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
Dario Freni2e8dffc2019-02-06 14:55:16 +0000180 * applied at next reboot.
181 *
182 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100183 * @throws PackageManagerException if call to apexd fails
Dario Freni2e8dffc2019-02-06 14:55:16 +0000184 */
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100185 abstract void markStagedSessionReady(int sessionId) throws PackageManagerException;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000186
187 /**
Nikita Ioffea820bd92019-02-15 14:22:44 +0000188 * Marks a staged session as successful.
189 *
190 * <p>Only activated session can be marked as successful.
191 *
192 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
193 * successful.
194 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100195 abstract void markStagedSessionSuccessful(int sessionId);
Nikita Ioffea820bd92019-02-15 14:22:44 +0000196
197 /**
Dario Freni83620602019-02-18 14:30:57 +0000198 * Whether the current device supports the management of APEX packages.
199 *
200 * @return true if APEX packages can be managed on this device, false otherwise.
201 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100202 abstract boolean isApexSupported();
Dario Freni83620602019-02-18 14:30:57 +0000203
204 /**
shafik07205e32019-02-07 20:12:33 +0000205 * Abandons the (only) active session previously submitted.
206 *
207 * @return {@code true} upon success, {@code false} if any remote exception occurs
208 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100209 abstract boolean abortActiveSession();
shafik07205e32019-02-07 20:12:33 +0000210
211 /**
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000212 * Abandons the staged session with the given sessionId.
213 *
214 * @return {@code true} upon success, {@code false} if any remote exception occurs
215 */
216 abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
217
218 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000219 * Uninstalls given {@code apexPackage}.
220 *
221 * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
222 *
223 * @param apexPackagePath package to uninstall.
224 * @return {@code true} upon successful uninstall, {@code false} otherwise.
225 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100226 abstract boolean uninstallApex(String apexPackagePath);
Gavin Corkeryef441722019-05-09 17:02:10 +0100227
228 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000229 * Dumps various state information to the provided {@link PrintWriter} object.
230 *
231 * @param pw the {@link PrintWriter} object to send information to.
232 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
233 * information about that specific package will be dumped.
234 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100235 abstract void dump(PrintWriter pw, @Nullable String packageName);
236
237 @IntDef(
238 flag = true,
239 prefix = { "MATCH_"},
240 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
241 @Retention(RetentionPolicy.SOURCE)
242 @interface PackageInfoFlags{}
243
244 /**
245 * An implementation of {@link ApexManager} that should be used in case device supports updating
246 * APEX packages.
247 */
Bill Lin57f19302019-10-09 18:35:20 +0800248 @VisibleForTesting
249 static class ApexManagerImpl extends ApexManager {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100250 private final IApexService mApexService;
251 private final Context mContext;
252 private final Object mLock = new Object();
253 /**
254 * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code
255 * AndroidManifest.xml}
256 *
257 * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
258 * AndroidManifest.xml}.
259 */
260 @GuardedBy("mLock")
261 private List<PackageInfo> mAllPackagesCache;
262
263 ApexManagerImpl(Context context, IApexService apexService) {
264 mContext = context;
265 mApexService = apexService;
266 }
267
268 /**
269 * Whether an APEX package is active or not.
270 *
271 * @param packageInfo the package to check
272 * @return {@code true} if this package is active, {@code false} otherwise.
273 */
274 private static boolean isActive(PackageInfo packageInfo) {
275 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
276 }
277
278 /**
279 * Whether the APEX package is pre-installed or not.
280 *
281 * @param packageInfo the package to check
282 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
283 */
284 private static boolean isFactory(PackageInfo packageInfo) {
285 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
286 }
287
288 @Override
Dario Frenic3e1bb722019-10-09 15:43:38 +0100289 List<ActiveApexInfo> getActiveApexInfos() {
290 try {
291 return Arrays.stream(mApexService.getActivePackages())
292 .map(apexInfo -> new ActiveApexInfo(
293 new File(
294 Environment.getApexDirectory() + File.separator
295 + apexInfo.moduleName),
Dario Freni5c611f32019-11-09 20:44:13 +0000296 new File(apexInfo.preinstalledModulePath))).collect(
Dario Frenic3e1bb722019-10-09 15:43:38 +0100297 Collectors.toList());
298 } catch (RemoteException e) {
299 Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
300 }
301 return Collections.emptyList();
302 }
303
304 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100305 void systemReady() {
306 mContext.registerReceiver(new BroadcastReceiver() {
307 @Override
308 public void onReceive(Context context, Intent intent) {
Nikita Ioffe8d325352019-08-23 18:26:31 +0100309 // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
310 // expensive to run it in broadcast handler thread.
311 BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100312 mContext.unregisterReceiver(this);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000313 }
Nikita Ioffe8d325352019-08-23 18:26:31 +0100314 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100315 }
316
317 private void populateAllPackagesCacheIfNeeded() {
318 synchronized (mLock) {
319 if (mAllPackagesCache != null) {
320 return;
321 }
322 try {
323 mAllPackagesCache = new ArrayList<>();
324 HashSet<String> activePackagesSet = new HashSet<>();
325 HashSet<String> factoryPackagesSet = new HashSet<>();
326 final ApexInfo[] allPkgs = mApexService.getAllPackages();
327 for (ApexInfo ai : allPkgs) {
328 // If the device is using flattened APEX, don't report any APEX
329 // packages since they won't be managed or updated by PackageManager.
330 if ((new File(ai.modulePath)).isDirectory()) {
331 break;
332 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100333 int flags = PackageManager.GET_META_DATA
334 | PackageManager.GET_SIGNING_CERTIFICATES
335 | PackageManager.GET_SIGNATURES;
336 PackageParser.Package pkg;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100337 try {
Oli Lanc2c7a222019-07-31 15:27:22 +0100338 File apexFile = new File(ai.modulePath);
339 PackageParser pp = new PackageParser();
340 pkg = pp.parsePackage(apexFile, flags, false);
341 PackageParser.collectCertificates(pkg, false);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100342 } catch (PackageParser.PackageParserException pe) {
343 throw new IllegalStateException("Unable to parse: " + ai, pe);
344 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100345
346 final PackageInfo packageInfo =
347 PackageParser.generatePackageInfo(pkg, ai, flags);
348 mAllPackagesCache.add(packageInfo);
349 if (ai.isActive) {
350 if (activePackagesSet.contains(packageInfo.packageName)) {
351 throw new IllegalStateException(
352 "Two active packages have the same name: "
353 + packageInfo.packageName);
354 }
355 activePackagesSet.add(packageInfo.packageName);
356 }
357 if (ai.isFactory) {
358 if (factoryPackagesSet.contains(packageInfo.packageName)) {
359 throw new IllegalStateException(
360 "Two factory packages have the same name: "
361 + packageInfo.packageName);
362 }
363 factoryPackagesSet.add(packageInfo.packageName);
364 }
365
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100366 }
367 } catch (RemoteException re) {
368 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
369 throw new RuntimeException(re);
370 }
371 }
372 }
373
374 @Override
375 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
376 populateAllPackagesCacheIfNeeded();
377 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
378 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
379 for (PackageInfo packageInfo: mAllPackagesCache) {
380 if (!packageInfo.packageName.equals(packageName)) {
381 continue;
382 }
Bill Lin4a352432019-10-09 16:22:25 +0800383 if ((matchActive && isActive(packageInfo))
384 || (matchFactory && isFactory(packageInfo))) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100385 return packageInfo;
386 }
387 }
388 return null;
389 }
390
391 @Override
392 List<PackageInfo> getActivePackages() {
393 populateAllPackagesCacheIfNeeded();
394 return mAllPackagesCache
395 .stream()
396 .filter(item -> isActive(item))
397 .collect(Collectors.toList());
398 }
399
400 @Override
401 List<PackageInfo> getFactoryPackages() {
402 populateAllPackagesCacheIfNeeded();
403 return mAllPackagesCache
404 .stream()
405 .filter(item -> isFactory(item))
406 .collect(Collectors.toList());
407 }
408
409 @Override
410 List<PackageInfo> getInactivePackages() {
411 populateAllPackagesCacheIfNeeded();
412 return mAllPackagesCache
413 .stream()
414 .filter(item -> !isActive(item))
415 .collect(Collectors.toList());
416 }
417
418 @Override
419 boolean isApexPackage(String packageName) {
420 if (!isApexSupported()) return false;
421 populateAllPackagesCacheIfNeeded();
422 for (PackageInfo packageInfo : mAllPackagesCache) {
423 if (packageInfo.packageName.equals(packageName)) {
424 return true;
425 }
426 }
427 return false;
428 }
429
430 @Override
431 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
432 try {
433 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
434 if (apexSessionInfo.isUnknown) {
435 return null;
436 }
437 return apexSessionInfo;
438 } catch (RemoteException re) {
439 Slog.e(TAG, "Unable to contact apexservice", re);
440 throw new RuntimeException(re);
441 }
442 }
443
444 @Override
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100445 ApexInfoList submitStagedSession(int sessionId, @NonNull int[] childSessionIds)
446 throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100447 try {
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100448 final ApexInfoList apexInfoList = new ApexInfoList();
449 mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList);
450 return apexInfoList;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100451 } catch (RemoteException re) {
452 Slog.e(TAG, "Unable to contact apexservice", re);
453 throw new RuntimeException(re);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100454 } catch (Exception e) {
455 throw new PackageManagerException(
456 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
457 "apexd verification failed : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100458 }
459 }
460
461 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100462 void markStagedSessionReady(int sessionId) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100463 try {
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100464 mApexService.markStagedSessionReady(sessionId);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100465 } catch (RemoteException re) {
466 Slog.e(TAG, "Unable to contact apexservice", re);
467 throw new RuntimeException(re);
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100468 } catch (Exception e) {
469 throw new PackageManagerException(
470 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
471 "Failed to mark apexd session as ready : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100472 }
473 }
474
475 @Override
476 void markStagedSessionSuccessful(int sessionId) {
477 try {
478 mApexService.markStagedSessionSuccessful(sessionId);
479 } catch (RemoteException re) {
480 Slog.e(TAG, "Unable to contact apexservice", re);
481 throw new RuntimeException(re);
482 } catch (Exception e) {
483 // It is fine to just log an exception in this case. APEXd will be able to recover
484 // in case markStagedSessionSuccessful fails.
485 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
486 }
487 }
488
489 @Override
490 boolean isApexSupported() {
491 return true;
492 }
493
494 @Override
495 boolean abortActiveSession() {
496 try {
497 mApexService.abortActiveSession();
498 return true;
499 } catch (RemoteException re) {
500 Slog.e(TAG, "Unable to contact apexservice", re);
501 return false;
502 }
503 }
504
505 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000506 boolean abortStagedSession(int sessionId) throws PackageManagerException {
507 try {
508 mApexService.abortStagedSession(sessionId);
509 return true;
510 } catch (RemoteException re) {
511 Slog.e(TAG, "Unable to contact apexservice", re);
512 return false;
513 } catch (Exception e) {
514 throw new PackageManagerException(
515 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
516 "Failed to abort staged session : " + e.getMessage());
517 }
518 }
519
520 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100521 boolean uninstallApex(String apexPackagePath) {
522 try {
523 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
524 return true;
525 } catch (Exception e) {
526 return false;
527 }
528 }
529
530 /**
531 * Dump information about the packages contained in a particular cache
532 * @param packagesCache the cache to print information about.
533 * @param packageName a {@link String} containing a package name, or {@code null}. If set,
534 * only information about that specific package will be dumped.
535 * @param ipw the {@link IndentingPrintWriter} object to send information to.
536 */
537 void dumpFromPackagesCache(
538 List<PackageInfo> packagesCache,
539 @Nullable String packageName,
540 IndentingPrintWriter ipw) {
541 ipw.println();
542 ipw.increaseIndent();
543 for (PackageInfo pi : packagesCache) {
544 if (packageName != null && !packageName.equals(pi.packageName)) {
545 continue;
546 }
547 ipw.println(pi.packageName);
548 ipw.increaseIndent();
549 ipw.println("Version: " + pi.versionCode);
550 ipw.println("Path: " + pi.applicationInfo.sourceDir);
551 ipw.println("IsActive: " + isActive(pi));
552 ipw.println("IsFactory: " + isFactory(pi));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000553 ipw.decreaseIndent();
554 }
555 ipw.decreaseIndent();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100556 ipw.println();
557 }
558
559 @Override
560 void dump(PrintWriter pw, @Nullable String packageName) {
561 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
562 try {
563 populateAllPackagesCacheIfNeeded();
564 ipw.println();
565 ipw.println("Active APEX packages:");
566 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
567 ipw.println("Inactive APEX packages:");
568 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
569 ipw.println("Factory APEX packages:");
570 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
571 ipw.increaseIndent();
572 ipw.println("APEX session state:");
573 ipw.increaseIndent();
574 final ApexSessionInfo[] sessions = mApexService.getSessions();
575 for (ApexSessionInfo si : sessions) {
576 ipw.println("Session ID: " + si.sessionId);
577 ipw.increaseIndent();
578 if (si.isUnknown) {
579 ipw.println("State: UNKNOWN");
580 } else if (si.isVerified) {
581 ipw.println("State: VERIFIED");
582 } else if (si.isStaged) {
583 ipw.println("State: STAGED");
584 } else if (si.isActivated) {
585 ipw.println("State: ACTIVATED");
586 } else if (si.isActivationFailed) {
587 ipw.println("State: ACTIVATION FAILED");
588 } else if (si.isSuccess) {
589 ipw.println("State: SUCCESS");
Mohammad Samiul Islam2dfc86a2019-11-20 13:53:07 +0000590 } else if (si.isRevertInProgress) {
591 ipw.println("State: REVERT IN PROGRESS");
592 } else if (si.isReverted) {
593 ipw.println("State: REVERTED");
594 } else if (si.isRevertFailed) {
595 ipw.println("State: REVERT FAILED");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100596 }
597 ipw.decreaseIndent();
598 }
599 ipw.decreaseIndent();
600 } catch (RemoteException e) {
601 ipw.println("Couldn't communicate with apexd.");
602 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000603 }
604 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000605
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100606 /**
607 * An implementation of {@link ApexManager} that should be used in case device does not support
608 * updating APEX packages.
609 */
Dario Frenic3e1bb722019-10-09 15:43:38 +0100610 private static final class ApexManagerFlattenedApex extends ApexManager {
611
612 @Override
613 List<ActiveApexInfo> getActiveApexInfos() {
614 // There is no apexd running in case of flattened apex
615 // We look up the /apex directory and identify the active APEX modules from there.
616 // As "preinstalled" path, we just report /system since in the case of flattened APEX
617 // the /apex directory is just a symlink to /system/apex.
618 List<ActiveApexInfo> result = new ArrayList<>();
619 File apexDir = Environment.getApexDirectory();
620 // In flattened configuration, init special-case the art directory and bind-mounts
621 // com.android.art.{release|debug} to com.android.art. At the time of writing, these
622 // directories are copied from the kArtApexDirNames variable in
623 // system/core/init/mount_namespace.cpp.
624 String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
625 if (apexDir.isDirectory()) {
626 File[] files = apexDir.listFiles();
627 // listFiles might be null if system server doesn't have permission to read
628 // a directory.
629 if (files != null) {
630 for (File file : files) {
631 if (file.isDirectory() && !file.getName().contains("@")) {
632 for (String skipDir : skipDirs) {
633 if (file.getName().equals(skipDir)) {
634 continue;
635 }
636 }
637 result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
638 }
639 }
640 }
641 }
642 return result;
643 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100644
645 @Override
646 void systemReady() {
647 // No-op
648 }
649
650 @Override
651 PackageInfo getPackageInfo(String packageName, int flags) {
652 return null;
653 }
654
655 @Override
656 List<PackageInfo> getActivePackages() {
657 return Collections.emptyList();
658 }
659
660 @Override
661 List<PackageInfo> getFactoryPackages() {
662 return Collections.emptyList();
663 }
664
665 @Override
666 List<PackageInfo> getInactivePackages() {
667 return Collections.emptyList();
668 }
669
670 @Override
671 boolean isApexPackage(String packageName) {
672 return false;
673 }
674
675 @Override
676 ApexSessionInfo getStagedSessionInfo(int sessionId) {
677 throw new UnsupportedOperationException();
678 }
679
680 @Override
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100681 ApexInfoList submitStagedSession(int sessionId, int[] childSessionIds)
682 throws PackageManagerException {
683 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
684 "Device doesn't support updating APEX");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100685 }
686
687 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100688 void markStagedSessionReady(int sessionId) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100689 throw new UnsupportedOperationException();
690 }
691
692 @Override
693 void markStagedSessionSuccessful(int sessionId) {
694 throw new UnsupportedOperationException();
695 }
696
697 @Override
698 boolean isApexSupported() {
699 return false;
700 }
701
702 @Override
703 boolean abortActiveSession() {
704 throw new UnsupportedOperationException();
705 }
706
707 @Override
Mohammad Samiul Islam44ad95b2019-11-20 15:14:36 +0000708 boolean abortStagedSession(int sessionId) throws PackageManagerException {
709 throw new UnsupportedOperationException();
710 }
711
712 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100713 boolean uninstallApex(String apexPackagePath) {
714 throw new UnsupportedOperationException();
715 }
716
717 @Override
718 void dump(PrintWriter pw, String packageName) {
719 // No-op
720 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000721 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900722}