blob: df189235704a833b01e2214792e327337016302b [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
14 * limitations under the License.s
15 */
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;
Mohammad Samiul Islam7aa7d2e2019-03-27 12:23:47 +000032import android.content.pm.PackageManager;
Dario Freni2e8dffc2019-02-06 14:55:16 +000033import android.content.pm.PackageParser;
34import android.content.pm.PackageParser.PackageParserException;
35import android.os.RemoteException;
36import android.os.ServiceManager;
Dario Frenia1c5f632019-02-14 15:37:54 +000037import android.os.ServiceManager.ServiceNotFoundException;
Dario Freni2ce84342019-04-26 13:06:22 +010038import android.sysprop.ApexProperties;
Nikita Ioffed3868dc2019-05-09 20:40:54 +010039import android.util.ArrayMap;
Dario Freni2e8dffc2019-02-06 14:55:16 +000040import android.util.Slog;
41
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000042import com.android.internal.annotations.GuardedBy;
Dario Freni2e8dffc2019-02-06 14:55:16 +000043import com.android.internal.util.IndentingPrintWriter;
44
45import java.io.File;
46import java.io.PrintWriter;
Gavin Corkeryef441722019-05-09 17:02:10 +010047import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
49import java.util.ArrayList;
Nikita Ioffef012a222019-03-05 22:37:55 +000050import java.util.Collections;
Gavin Corkeryef441722019-05-09 17:02:10 +010051import java.util.HashSet;
52import java.util.List;
53import java.util.stream.Collectors;
Dario Freni2e8dffc2019-02-06 14:55:16 +000054
55/**
56 * ApexManager class handles communications with the apex service to perform operation and queries,
57 * as well as providing caching to avoid unnecessary calls to the service.
58 */
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000059class ApexManager {
60 static final String TAG = "ApexManager";
61 private final IApexService mApexService;
62 private final Context mContext;
63 private final Object mLock = new Object();
Nikita Ioffed3868dc2019-05-09 20:40:54 +010064 /**
65 * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code
66 * AndroidManifest.xml}
67 *
68 * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
Gavin Corkeryef441722019-05-09 17:02:10 +010069 * AndroidManifest.xml}.
Nikita Ioffed3868dc2019-05-09 20:40:54 +010070 */
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000071 @GuardedBy("mLock")
Gavin Corkeryef441722019-05-09 17:02:10 +010072 private List<PackageInfo> mAllPackagesCache;
Nikita Ioffed3868dc2019-05-09 20:40:54 +010073 /**
74 * A map from {@code apexName} to the {@Link PackageInfo} generated from the {@code
75 * AndroidManifest.xml}.
76 *
77 * <p>Note that key of this map is {@code apexName} field which corresponds to the {@code name}
78 * field of {@code apex_manifest.json}.
79 */
80 // TODO(b/132324953): remove.
81 @GuardedBy("mLock")
82 private ArrayMap<String, PackageInfo> mApexNameToPackageInfoCache;
Dario Freni2e8dffc2019-02-06 14:55:16 +000083
Gavin Corkeryef441722019-05-09 17:02:10 +010084
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000085 ApexManager(Context context) {
Jiyong Parkc53878f2019-05-25 01:31:32 +090086 mContext = context;
87 if (!isApexSupported()) {
88 mApexService = null;
89 return;
90 }
Dario Frenia1c5f632019-02-14 15:37:54 +000091 try {
92 mApexService = IApexService.Stub.asInterface(
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000093 ServiceManager.getServiceOrThrow("apexservice"));
Dario Frenia1c5f632019-02-14 15:37:54 +000094 } catch (ServiceNotFoundException e) {
95 throw new IllegalStateException("Required service apexservice not available");
96 }
Dario Freni14f885b2019-02-25 12:48:47 +000097 }
98
Gavin Corkeryef441722019-05-09 17:02:10 +010099 static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
100 static final int MATCH_FACTORY_PACKAGE = 1 << 1;
101 @IntDef(
102 flag = true,
103 prefix = { "MATCH_"},
104 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
105 @Retention(RetentionPolicy.SOURCE)
106 @interface PackageInfoFlags{}
107
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000108 void systemReady() {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900109 if (!isApexSupported()) return;
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000110 mContext.registerReceiver(new BroadcastReceiver() {
111 @Override
112 public void onReceive(Context context, Intent intent) {
113 onBootCompleted();
114 mContext.unregisterReceiver(this);
115 }
116 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000117 }
118
Gavin Corkeryef441722019-05-09 17:02:10 +0100119 private void populateAllPackagesCacheIfNeeded() {
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000120 synchronized (mLock) {
Gavin Corkeryef441722019-05-09 17:02:10 +0100121 if (mAllPackagesCache != null) {
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000122 return;
123 }
Nikita Ioffed3868dc2019-05-09 20:40:54 +0100124 mApexNameToPackageInfoCache = new ArrayMap<>();
Martijn Coenen3f629942019-02-22 13:15:46 +0100125 try {
Gavin Corkeryef441722019-05-09 17:02:10 +0100126 mAllPackagesCache = new ArrayList<>();
127 HashSet<String> activePackagesSet = new HashSet<>();
128 HashSet<String> factoryPackagesSet = new HashSet<>();
129 final ApexInfo[] allPkgs = mApexService.getAllPackages();
130 for (ApexInfo ai : allPkgs) {
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000131 // If the device is using flattened APEX, don't report any APEX
132 // packages since they won't be managed or updated by PackageManager.
133 if ((new File(ai.packagePath)).isDirectory()) {
134 break;
135 }
136 try {
Nikita Ioffed3868dc2019-05-09 20:40:54 +0100137 final PackageInfo pkg = PackageParser.generatePackageInfoFromApex(
Gavin Corkeryef441722019-05-09 17:02:10 +0100138 ai, PackageManager.GET_META_DATA
139 | PackageManager.GET_SIGNING_CERTIFICATES);
140 mAllPackagesCache.add(pkg);
141 if (ai.isActive) {
142 if (activePackagesSet.contains(pkg.packageName)) {
143 throw new IllegalStateException(
144 "Two active packages have the same name: "
145 + pkg.packageName);
146 }
147 activePackagesSet.add(ai.packageName);
148 // TODO(b/132324953): remove.
149 mApexNameToPackageInfoCache.put(ai.packageName, pkg);
150 }
151 if (ai.isFactory) {
152 if (factoryPackagesSet.contains(pkg.packageName)) {
153 throw new IllegalStateException(
154 "Two factory packages have the same name: "
155 + pkg.packageName);
156 }
157 factoryPackagesSet.add(ai.packageName);
158 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000159 } catch (PackageParserException pe) {
160 throw new IllegalStateException("Unable to parse: " + ai, pe);
161 }
162 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000163 } catch (RemoteException re) {
164 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
165 throw new RuntimeException(re);
Martijn Coenen3f629942019-02-22 13:15:46 +0100166 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000167 }
168 }
169
170 /**
Gavin Corkeryef441722019-05-09 17:02:10 +0100171 * Retrieves information about an APEX package.
Dario Freni2e8dffc2019-02-06 14:55:16 +0000172 *
173 * @param packageName the package name to look for. Note that this is the package name reported
174 * in the APK container manifest (i.e. AndroidManifest.xml), which might
175 * differ from the one reported in the APEX manifest (i.e.
176 * apex_manifest.json).
Gavin Corkeryef441722019-05-09 17:02:10 +0100177 * @param flags the type of package to return. This may match to active packages
178 * and factory (pre-installed) packages.
Dario Freni2e8dffc2019-02-06 14:55:16 +0000179 * @return a PackageInfo object with the information about the package, or null if the package
180 * is not found.
181 */
Gavin Corkeryef441722019-05-09 17:02:10 +0100182 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900183 if (!isApexSupported()) return null;
Gavin Corkeryef441722019-05-09 17:02:10 +0100184 populateAllPackagesCacheIfNeeded();
185 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
186 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
187 for (PackageInfo packageInfo: mAllPackagesCache) {
188 if (!packageInfo.packageName.equals(packageName)) {
189 continue;
190 }
191 if ((!matchActive || isActive(packageInfo))
192 && (!matchFactory || isFactory(packageInfo))) {
193 return packageInfo;
194 }
195 }
196 return null;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000197 }
198
199 /**
Gavin Corkeryef441722019-05-09 17:02:10 +0100200 * Returns a {@link PackageInfo} for an active APEX package keyed by it's {@code apexName}.
Nikita Ioffed3868dc2019-05-09 20:40:54 +0100201 *
202 * @deprecated this API will soon be deleted, please don't depend on it.
203 */
204 // TODO(b/132324953): delete.
205 @Deprecated
206 @Nullable PackageInfo getPackageInfoForApexName(String apexName) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900207 if (!isApexSupported()) return null;
Gavin Corkeryef441722019-05-09 17:02:10 +0100208 populateAllPackagesCacheIfNeeded();
Nikita Ioffed3868dc2019-05-09 20:40:54 +0100209 return mApexNameToPackageInfoCache.get(apexName);
210 }
211
212 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000213 * Retrieves information about all active APEX packages.
214 *
Gavin Corkeryef441722019-05-09 17:02:10 +0100215 * @return a List of PackageInfo object, each one containing information about a different
Dario Freni2e8dffc2019-02-06 14:55:16 +0000216 * active package.
217 */
Gavin Corkeryef441722019-05-09 17:02:10 +0100218 List<PackageInfo> getActivePackages() {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900219 if (!isApexSupported()) return Collections.emptyList();
Gavin Corkeryef441722019-05-09 17:02:10 +0100220 populateAllPackagesCacheIfNeeded();
221 return mAllPackagesCache
222 .stream()
223 .filter(item -> isActive(item))
224 .collect(Collectors.toList());
225 }
226
227 /**
228 * Retrieves information about all active pre-installed APEX packages.
229 *
230 * @return a List of PackageInfo object, each one containing information about a different
231 * active pre-installed package.
232 */
233 List<PackageInfo> getFactoryPackages() {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900234 if (!isApexSupported()) return Collections.emptyList();
Gavin Corkeryef441722019-05-09 17:02:10 +0100235 populateAllPackagesCacheIfNeeded();
236 return mAllPackagesCache
237 .stream()
238 .filter(item -> isFactory(item))
239 .collect(Collectors.toList());
240 }
241
242 /**
243 * Retrieves information about all inactive APEX packages.
244 *
245 * @return a List of PackageInfo object, each one containing information about a different
246 * inactive package.
247 */
248 List<PackageInfo> getInactivePackages() {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900249 if (!isApexSupported()) return Collections.emptyList();
Gavin Corkeryef441722019-05-09 17:02:10 +0100250 populateAllPackagesCacheIfNeeded();
251 return mAllPackagesCache
252 .stream()
253 .filter(item -> !isActive(item))
254 .collect(Collectors.toList());
Dario Freni2e8dffc2019-02-06 14:55:16 +0000255 }
256
257 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000258 * Checks if {@code packageName} is an apex package.
259 *
260 * @param packageName package to check.
261 * @return {@code true} if {@code packageName} is an apex package.
262 */
263 boolean isApexPackage(String packageName) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900264 if (!isApexSupported()) return false;
Gavin Corkeryef441722019-05-09 17:02:10 +0100265 populateAllPackagesCacheIfNeeded();
266 for (PackageInfo packageInfo : mAllPackagesCache) {
267 if (packageInfo.packageName.equals(packageName)) {
268 return true;
269 }
270 }
271 return false;
Nikita Ioffef012a222019-03-05 22:37:55 +0000272 }
273
274 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000275 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
276 * track the different states of a session.
277 *
278 * @param sessionId the identifier of the session.
279 * @return an ApexSessionInfo object, or null if the session is not known.
280 */
281 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900282 if (!isApexSupported()) return null;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000283 try {
284 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
285 if (apexSessionInfo.isUnknown) {
286 return null;
287 }
288 return apexSessionInfo;
289 } catch (RemoteException re) {
290 Slog.e(TAG, "Unable to contact apexservice", re);
291 throw new RuntimeException(re);
292 }
293 }
294
295 /**
296 * Submit a staged session to apex service. This causes the apex service to perform some initial
297 * verification and accept or reject the session. Submitting a session successfully is not
298 * enough for it to be activated at the next boot, the caller needs to call
299 * {@link #markStagedSessionReady(int)}.
300 *
301 * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted.
302 * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain
303 * an array of identifiers of all the child sessions. Otherwise it should
304 * be an empty array.
305 * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller
306 * and will be filled with a list of {@link ApexInfo} objects, each of which
307 * contains metadata about one of the packages being submitted as part of
308 * the session.
309 * @return whether the submission of the session was successful.
310 */
311 boolean submitStagedSession(
312 int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900313 if (!isApexSupported()) return false;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000314 try {
315 return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList);
316 } catch (RemoteException re) {
317 Slog.e(TAG, "Unable to contact apexservice", re);
318 throw new RuntimeException(re);
319 }
320 }
321
322 /**
Dario Frenia0e3dda2019-02-18 20:58:54 +0000323 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
Dario Freni2e8dffc2019-02-06 14:55:16 +0000324 * applied at next reboot.
325 *
326 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
327 * @return true upon success, false if the session is unknown.
328 */
329 boolean markStagedSessionReady(int sessionId) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900330 if (!isApexSupported()) return false;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000331 try {
332 return mApexService.markStagedSessionReady(sessionId);
333 } catch (RemoteException re) {
334 Slog.e(TAG, "Unable to contact apexservice", re);
335 throw new RuntimeException(re);
336 }
337 }
338
339 /**
Nikita Ioffea820bd92019-02-15 14:22:44 +0000340 * Marks a staged session as successful.
341 *
342 * <p>Only activated session can be marked as successful.
343 *
344 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
345 * successful.
346 */
347 void markStagedSessionSuccessful(int sessionId) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900348 if (!isApexSupported()) return;
Nikita Ioffea820bd92019-02-15 14:22:44 +0000349 try {
350 mApexService.markStagedSessionSuccessful(sessionId);
351 } catch (RemoteException re) {
352 Slog.e(TAG, "Unable to contact apexservice", re);
353 throw new RuntimeException(re);
354 } catch (Exception e) {
355 // It is fine to just log an exception in this case. APEXd will be able to recover in
356 // case markStagedSessionSuccessful fails.
357 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
358 }
359 }
360
361 /**
Dario Freni83620602019-02-18 14:30:57 +0000362 * Whether the current device supports the management of APEX packages.
363 *
364 * @return true if APEX packages can be managed on this device, false otherwise.
365 */
366 boolean isApexSupported() {
Dario Freni2ce84342019-04-26 13:06:22 +0100367 return ApexProperties.updatable().orElse(false);
Dario Freni83620602019-02-18 14:30:57 +0000368 }
369
370 /**
shafik07205e32019-02-07 20:12:33 +0000371 * Abandons the (only) active session previously submitted.
372 *
373 * @return {@code true} upon success, {@code false} if any remote exception occurs
374 */
375 boolean abortActiveSession() {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900376 if (!isApexSupported()) return false;
shafik07205e32019-02-07 20:12:33 +0000377 try {
378 mApexService.abortActiveSession();
379 return true;
380 } catch (RemoteException re) {
381 Slog.e(TAG, "Unable to contact apexservice", re);
382 return false;
383 }
384 }
385
386 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000387 * Uninstalls given {@code apexPackage}.
388 *
389 * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
390 *
391 * @param apexPackagePath package to uninstall.
392 * @return {@code true} upon successful uninstall, {@code false} otherwise.
393 */
394 boolean uninstallApex(String apexPackagePath) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900395 if (!isApexSupported()) return false;
Nikita Ioffef012a222019-03-05 22:37:55 +0000396 try {
397 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
398 return true;
399 } catch (Exception e) {
400 return false;
401 }
402 }
403
404 /**
Gavin Corkeryef441722019-05-09 17:02:10 +0100405 * Whether an APEX package is active or not.
406 *
407 * @param packageInfo the package to check
408 * @return {@code true} if this package is active, {@code false} otherwise.
409 */
410 private static boolean isActive(PackageInfo packageInfo) {
411 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
412 }
413
414 /**
415 * Whether the APEX package is pre-installed or not.
416 *
417 * @param packageInfo the package to check
418 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
419 */
420 private static boolean isFactory(PackageInfo packageInfo) {
421 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
422 }
423
424 /**
425 * Dump information about the packages contained in a particular cache
426 * @param packagesCache the cache to print information about.
427 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
428 * information about that specific package will be dumped.
429 * @param ipw the {@link IndentingPrintWriter} object to send information to.
430 */
431 void dumpFromPackagesCache(
432 List<PackageInfo> packagesCache,
433 @Nullable String packageName,
434 IndentingPrintWriter ipw) {
435 ipw.println();
436 ipw.increaseIndent();
437 for (PackageInfo pi : packagesCache) {
438 if (packageName != null && !packageName.equals(pi.packageName)) {
439 continue;
440 }
441 ipw.println(pi.packageName);
442 ipw.increaseIndent();
443 ipw.println("Version: " + pi.versionCode);
444 ipw.println("Path: " + pi.applicationInfo.sourceDir);
445 ipw.println("IsActive: " + isActive(pi));
446 ipw.println("IsFactory: " + isFactory(pi));
447 ipw.decreaseIndent();
448 }
449 ipw.decreaseIndent();
450 ipw.println();
451 }
452
453 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000454 * Dumps various state information to the provided {@link PrintWriter} object.
455 *
456 * @param pw the {@link PrintWriter} object to send information to.
457 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
458 * information about that specific package will be dumped.
459 */
460 void dump(PrintWriter pw, @Nullable String packageName) {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900461 if (!isApexSupported()) return;
Dario Freni2e8dffc2019-02-06 14:55:16 +0000462 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000463 try {
Gavin Corkeryef441722019-05-09 17:02:10 +0100464 populateAllPackagesCacheIfNeeded();
Dario Freni2e8dffc2019-02-06 14:55:16 +0000465 ipw.println();
Gavin Corkeryef441722019-05-09 17:02:10 +0100466 ipw.println("Active APEX packages:");
467 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
468 ipw.println("Inactive APEX packages:");
469 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
470 ipw.println("Factory APEX packages:");
471 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
472 ipw.increaseIndent();
Dario Freni2e8dffc2019-02-06 14:55:16 +0000473 ipw.println("APEX session state:");
474 ipw.increaseIndent();
475 final ApexSessionInfo[] sessions = mApexService.getSessions();
476 for (ApexSessionInfo si : sessions) {
Nikita Ioffea820bd92019-02-15 14:22:44 +0000477 ipw.println("Session ID: " + si.sessionId);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000478 ipw.increaseIndent();
479 if (si.isUnknown) {
480 ipw.println("State: UNKNOWN");
481 } else if (si.isVerified) {
482 ipw.println("State: VERIFIED");
483 } else if (si.isStaged) {
484 ipw.println("State: STAGED");
485 } else if (si.isActivated) {
486 ipw.println("State: ACTIVATED");
Dario Freni2e8dffc2019-02-06 14:55:16 +0000487 } else if (si.isActivationFailed) {
488 ipw.println("State: ACTIVATION FAILED");
Nikita Ioffe82742222019-02-26 12:14:04 +0000489 } else if (si.isSuccess) {
490 ipw.println("State: SUCCESS");
491 } else if (si.isRollbackInProgress) {
492 ipw.println("State: ROLLBACK IN PROGRESS");
493 } else if (si.isRolledBack) {
494 ipw.println("State: ROLLED BACK");
Nikita Ioffe7a4f08e2019-04-09 15:10:18 +0100495 } else if (si.isRollbackFailed) {
496 ipw.println("State: ROLLBACK FAILED");
Dario Freni2e8dffc2019-02-06 14:55:16 +0000497 }
498 ipw.decreaseIndent();
499 }
500 ipw.decreaseIndent();
501 } catch (RemoteException e) {
502 ipw.println("Couldn't communicate with apexd.");
503 }
504 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000505
506 public void onBootCompleted() {
Jiyong Parkc53878f2019-05-25 01:31:32 +0900507 if (!isApexSupported()) return;
Gavin Corkeryef441722019-05-09 17:02:10 +0100508 populateAllPackagesCacheIfNeeded();
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000509 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900510}