blob: 44c36ee01bbfe10cfe8eb2a375db6c23938b713e [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 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000212 * Uninstalls given {@code apexPackage}.
213 *
214 * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
215 *
216 * @param apexPackagePath package to uninstall.
217 * @return {@code true} upon successful uninstall, {@code false} otherwise.
218 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100219 abstract boolean uninstallApex(String apexPackagePath);
Gavin Corkeryef441722019-05-09 17:02:10 +0100220
221 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000222 * Dumps various state information to the provided {@link PrintWriter} object.
223 *
224 * @param pw the {@link PrintWriter} object to send information to.
225 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
226 * information about that specific package will be dumped.
227 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100228 abstract void dump(PrintWriter pw, @Nullable String packageName);
229
230 @IntDef(
231 flag = true,
232 prefix = { "MATCH_"},
233 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
234 @Retention(RetentionPolicy.SOURCE)
235 @interface PackageInfoFlags{}
236
237 /**
238 * An implementation of {@link ApexManager} that should be used in case device supports updating
239 * APEX packages.
240 */
Bill Lin57f19302019-10-09 18:35:20 +0800241 @VisibleForTesting
242 static class ApexManagerImpl extends ApexManager {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100243 private final IApexService mApexService;
244 private final Context mContext;
245 private final Object mLock = new Object();
246 /**
247 * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code
248 * AndroidManifest.xml}
249 *
250 * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
251 * AndroidManifest.xml}.
252 */
253 @GuardedBy("mLock")
254 private List<PackageInfo> mAllPackagesCache;
255
256 ApexManagerImpl(Context context, IApexService apexService) {
257 mContext = context;
258 mApexService = apexService;
259 }
260
261 /**
262 * Whether an APEX package is active or not.
263 *
264 * @param packageInfo the package to check
265 * @return {@code true} if this package is active, {@code false} otherwise.
266 */
267 private static boolean isActive(PackageInfo packageInfo) {
268 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
269 }
270
271 /**
272 * Whether the APEX package is pre-installed or not.
273 *
274 * @param packageInfo the package to check
275 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
276 */
277 private static boolean isFactory(PackageInfo packageInfo) {
278 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
279 }
280
281 @Override
Dario Frenic3e1bb722019-10-09 15:43:38 +0100282 List<ActiveApexInfo> getActiveApexInfos() {
283 try {
284 return Arrays.stream(mApexService.getActivePackages())
285 .map(apexInfo -> new ActiveApexInfo(
286 new File(
287 Environment.getApexDirectory() + File.separator
288 + apexInfo.moduleName),
Dario Freni5c611f32019-11-09 20:44:13 +0000289 new File(apexInfo.preinstalledModulePath))).collect(
Dario Frenic3e1bb722019-10-09 15:43:38 +0100290 Collectors.toList());
291 } catch (RemoteException e) {
292 Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
293 }
294 return Collections.emptyList();
295 }
296
297 @Override
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100298 void systemReady() {
299 mContext.registerReceiver(new BroadcastReceiver() {
300 @Override
301 public void onReceive(Context context, Intent intent) {
Nikita Ioffe8d325352019-08-23 18:26:31 +0100302 // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
303 // expensive to run it in broadcast handler thread.
304 BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100305 mContext.unregisterReceiver(this);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000306 }
Nikita Ioffe8d325352019-08-23 18:26:31 +0100307 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100308 }
309
310 private void populateAllPackagesCacheIfNeeded() {
311 synchronized (mLock) {
312 if (mAllPackagesCache != null) {
313 return;
314 }
315 try {
316 mAllPackagesCache = new ArrayList<>();
317 HashSet<String> activePackagesSet = new HashSet<>();
318 HashSet<String> factoryPackagesSet = new HashSet<>();
319 final ApexInfo[] allPkgs = mApexService.getAllPackages();
320 for (ApexInfo ai : allPkgs) {
321 // If the device is using flattened APEX, don't report any APEX
322 // packages since they won't be managed or updated by PackageManager.
323 if ((new File(ai.modulePath)).isDirectory()) {
324 break;
325 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100326 int flags = PackageManager.GET_META_DATA
327 | PackageManager.GET_SIGNING_CERTIFICATES
328 | PackageManager.GET_SIGNATURES;
329 PackageParser.Package pkg;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100330 try {
Oli Lanc2c7a222019-07-31 15:27:22 +0100331 File apexFile = new File(ai.modulePath);
332 PackageParser pp = new PackageParser();
333 pkg = pp.parsePackage(apexFile, flags, false);
334 PackageParser.collectCertificates(pkg, false);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100335 } catch (PackageParser.PackageParserException pe) {
336 throw new IllegalStateException("Unable to parse: " + ai, pe);
337 }
Oli Lanc2c7a222019-07-31 15:27:22 +0100338
339 final PackageInfo packageInfo =
340 PackageParser.generatePackageInfo(pkg, ai, flags);
341 mAllPackagesCache.add(packageInfo);
342 if (ai.isActive) {
343 if (activePackagesSet.contains(packageInfo.packageName)) {
344 throw new IllegalStateException(
345 "Two active packages have the same name: "
346 + packageInfo.packageName);
347 }
348 activePackagesSet.add(packageInfo.packageName);
349 }
350 if (ai.isFactory) {
351 if (factoryPackagesSet.contains(packageInfo.packageName)) {
352 throw new IllegalStateException(
353 "Two factory packages have the same name: "
354 + packageInfo.packageName);
355 }
356 factoryPackagesSet.add(packageInfo.packageName);
357 }
358
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100359 }
360 } catch (RemoteException re) {
361 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
362 throw new RuntimeException(re);
363 }
364 }
365 }
366
367 @Override
368 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
369 populateAllPackagesCacheIfNeeded();
370 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
371 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
372 for (PackageInfo packageInfo: mAllPackagesCache) {
373 if (!packageInfo.packageName.equals(packageName)) {
374 continue;
375 }
Bill Lin4a352432019-10-09 16:22:25 +0800376 if ((matchActive && isActive(packageInfo))
377 || (matchFactory && isFactory(packageInfo))) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100378 return packageInfo;
379 }
380 }
381 return null;
382 }
383
384 @Override
385 List<PackageInfo> getActivePackages() {
386 populateAllPackagesCacheIfNeeded();
387 return mAllPackagesCache
388 .stream()
389 .filter(item -> isActive(item))
390 .collect(Collectors.toList());
391 }
392
393 @Override
394 List<PackageInfo> getFactoryPackages() {
395 populateAllPackagesCacheIfNeeded();
396 return mAllPackagesCache
397 .stream()
398 .filter(item -> isFactory(item))
399 .collect(Collectors.toList());
400 }
401
402 @Override
403 List<PackageInfo> getInactivePackages() {
404 populateAllPackagesCacheIfNeeded();
405 return mAllPackagesCache
406 .stream()
407 .filter(item -> !isActive(item))
408 .collect(Collectors.toList());
409 }
410
411 @Override
412 boolean isApexPackage(String packageName) {
413 if (!isApexSupported()) return false;
414 populateAllPackagesCacheIfNeeded();
415 for (PackageInfo packageInfo : mAllPackagesCache) {
416 if (packageInfo.packageName.equals(packageName)) {
417 return true;
418 }
419 }
420 return false;
421 }
422
423 @Override
424 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
425 try {
426 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
427 if (apexSessionInfo.isUnknown) {
428 return null;
429 }
430 return apexSessionInfo;
431 } catch (RemoteException re) {
432 Slog.e(TAG, "Unable to contact apexservice", re);
433 throw new RuntimeException(re);
434 }
435 }
436
437 @Override
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100438 ApexInfoList submitStagedSession(int sessionId, @NonNull int[] childSessionIds)
439 throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100440 try {
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100441 final ApexInfoList apexInfoList = new ApexInfoList();
442 mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList);
443 return apexInfoList;
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100444 } catch (RemoteException re) {
445 Slog.e(TAG, "Unable to contact apexservice", re);
446 throw new RuntimeException(re);
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100447 } catch (Exception e) {
448 throw new PackageManagerException(
449 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
450 "apexd verification failed : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100451 }
452 }
453
454 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100455 void markStagedSessionReady(int sessionId) throws PackageManagerException {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100456 try {
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100457 mApexService.markStagedSessionReady(sessionId);
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100458 } catch (RemoteException re) {
459 Slog.e(TAG, "Unable to contact apexservice", re);
460 throw new RuntimeException(re);
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100461 } catch (Exception e) {
462 throw new PackageManagerException(
463 PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
464 "Failed to mark apexd session as ready : " + e.getMessage());
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100465 }
466 }
467
468 @Override
469 void markStagedSessionSuccessful(int sessionId) {
470 try {
471 mApexService.markStagedSessionSuccessful(sessionId);
472 } catch (RemoteException re) {
473 Slog.e(TAG, "Unable to contact apexservice", re);
474 throw new RuntimeException(re);
475 } catch (Exception e) {
476 // It is fine to just log an exception in this case. APEXd will be able to recover
477 // in case markStagedSessionSuccessful fails.
478 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
479 }
480 }
481
482 @Override
483 boolean isApexSupported() {
484 return true;
485 }
486
487 @Override
488 boolean abortActiveSession() {
489 try {
490 mApexService.abortActiveSession();
491 return true;
492 } catch (RemoteException re) {
493 Slog.e(TAG, "Unable to contact apexservice", re);
494 return false;
495 }
496 }
497
498 @Override
499 boolean uninstallApex(String apexPackagePath) {
500 try {
501 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
502 return true;
503 } catch (Exception e) {
504 return false;
505 }
506 }
507
508 /**
509 * Dump information about the packages contained in a particular cache
510 * @param packagesCache the cache to print information about.
511 * @param packageName a {@link String} containing a package name, or {@code null}. If set,
512 * only information about that specific package will be dumped.
513 * @param ipw the {@link IndentingPrintWriter} object to send information to.
514 */
515 void dumpFromPackagesCache(
516 List<PackageInfo> packagesCache,
517 @Nullable String packageName,
518 IndentingPrintWriter ipw) {
519 ipw.println();
520 ipw.increaseIndent();
521 for (PackageInfo pi : packagesCache) {
522 if (packageName != null && !packageName.equals(pi.packageName)) {
523 continue;
524 }
525 ipw.println(pi.packageName);
526 ipw.increaseIndent();
527 ipw.println("Version: " + pi.versionCode);
528 ipw.println("Path: " + pi.applicationInfo.sourceDir);
529 ipw.println("IsActive: " + isActive(pi));
530 ipw.println("IsFactory: " + isFactory(pi));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000531 ipw.decreaseIndent();
532 }
533 ipw.decreaseIndent();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100534 ipw.println();
535 }
536
537 @Override
538 void dump(PrintWriter pw, @Nullable String packageName) {
539 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
540 try {
541 populateAllPackagesCacheIfNeeded();
542 ipw.println();
543 ipw.println("Active APEX packages:");
544 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
545 ipw.println("Inactive APEX packages:");
546 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
547 ipw.println("Factory APEX packages:");
548 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
549 ipw.increaseIndent();
550 ipw.println("APEX session state:");
551 ipw.increaseIndent();
552 final ApexSessionInfo[] sessions = mApexService.getSessions();
553 for (ApexSessionInfo si : sessions) {
554 ipw.println("Session ID: " + si.sessionId);
555 ipw.increaseIndent();
556 if (si.isUnknown) {
557 ipw.println("State: UNKNOWN");
558 } else if (si.isVerified) {
559 ipw.println("State: VERIFIED");
560 } else if (si.isStaged) {
561 ipw.println("State: STAGED");
562 } else if (si.isActivated) {
563 ipw.println("State: ACTIVATED");
564 } else if (si.isActivationFailed) {
565 ipw.println("State: ACTIVATION FAILED");
566 } else if (si.isSuccess) {
567 ipw.println("State: SUCCESS");
Mohammad Samiul Islam2dfc86a2019-11-20 13:53:07 +0000568 } else if (si.isRevertInProgress) {
569 ipw.println("State: REVERT IN PROGRESS");
570 } else if (si.isReverted) {
571 ipw.println("State: REVERTED");
572 } else if (si.isRevertFailed) {
573 ipw.println("State: REVERT FAILED");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100574 }
575 ipw.decreaseIndent();
576 }
577 ipw.decreaseIndent();
578 } catch (RemoteException e) {
579 ipw.println("Couldn't communicate with apexd.");
580 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000581 }
582 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000583
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100584 /**
585 * An implementation of {@link ApexManager} that should be used in case device does not support
586 * updating APEX packages.
587 */
Dario Frenic3e1bb722019-10-09 15:43:38 +0100588 private static final class ApexManagerFlattenedApex extends ApexManager {
589
590 @Override
591 List<ActiveApexInfo> getActiveApexInfos() {
592 // There is no apexd running in case of flattened apex
593 // We look up the /apex directory and identify the active APEX modules from there.
594 // As "preinstalled" path, we just report /system since in the case of flattened APEX
595 // the /apex directory is just a symlink to /system/apex.
596 List<ActiveApexInfo> result = new ArrayList<>();
597 File apexDir = Environment.getApexDirectory();
598 // In flattened configuration, init special-case the art directory and bind-mounts
599 // com.android.art.{release|debug} to com.android.art. At the time of writing, these
600 // directories are copied from the kArtApexDirNames variable in
601 // system/core/init/mount_namespace.cpp.
602 String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
603 if (apexDir.isDirectory()) {
604 File[] files = apexDir.listFiles();
605 // listFiles might be null if system server doesn't have permission to read
606 // a directory.
607 if (files != null) {
608 for (File file : files) {
609 if (file.isDirectory() && !file.getName().contains("@")) {
610 for (String skipDir : skipDirs) {
611 if (file.getName().equals(skipDir)) {
612 continue;
613 }
614 }
615 result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
616 }
617 }
618 }
619 }
620 return result;
621 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100622
623 @Override
624 void systemReady() {
625 // No-op
626 }
627
628 @Override
629 PackageInfo getPackageInfo(String packageName, int flags) {
630 return null;
631 }
632
633 @Override
634 List<PackageInfo> getActivePackages() {
635 return Collections.emptyList();
636 }
637
638 @Override
639 List<PackageInfo> getFactoryPackages() {
640 return Collections.emptyList();
641 }
642
643 @Override
644 List<PackageInfo> getInactivePackages() {
645 return Collections.emptyList();
646 }
647
648 @Override
649 boolean isApexPackage(String packageName) {
650 return false;
651 }
652
653 @Override
654 ApexSessionInfo getStagedSessionInfo(int sessionId) {
655 throw new UnsupportedOperationException();
656 }
657
658 @Override
Nikita Ioffe4e7d24a2019-07-05 15:49:45 +0100659 ApexInfoList submitStagedSession(int sessionId, int[] childSessionIds)
660 throws PackageManagerException {
661 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
662 "Device doesn't support updating APEX");
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100663 }
664
665 @Override
Nikita Ioffeab4d7352019-07-09 20:48:54 +0100666 void markStagedSessionReady(int sessionId) {
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100667 throw new UnsupportedOperationException();
668 }
669
670 @Override
671 void markStagedSessionSuccessful(int sessionId) {
672 throw new UnsupportedOperationException();
673 }
674
675 @Override
676 boolean isApexSupported() {
677 return false;
678 }
679
680 @Override
681 boolean abortActiveSession() {
682 throw new UnsupportedOperationException();
683 }
684
685 @Override
686 boolean uninstallApex(String apexPackagePath) {
687 throw new UnsupportedOperationException();
688 }
689
690 @Override
691 void dump(PrintWriter pw, String packageName) {
692 // No-op
693 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000694 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900695}