blob: ac79a256f06e6d34c22152f3b9e6abc0ab66c5eb [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
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.apex.ApexInfo;
22import android.apex.ApexInfoList;
23import android.apex.ApexSessionInfo;
24import android.apex.IApexService;
25import android.content.pm.PackageInfo;
26import android.content.pm.PackageParser;
27import android.content.pm.PackageParser.PackageParserException;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.util.Slog;
31
32import com.android.internal.util.IndentingPrintWriter;
33
34import java.io.File;
35import java.io.PrintWriter;
36import java.util.ArrayList;
37import java.util.Collection;
38import java.util.List;
39import java.util.Map;
40import java.util.function.Function;
41import java.util.stream.Collectors;
42
43/**
44 * ApexManager class handles communications with the apex service to perform operation and queries,
45 * as well as providing caching to avoid unnecessary calls to the service.
46 */
47class ApexManager {
48 static final String TAG = "ApexManager";
49 private final IApexService mApexService;
50 private final Map<String, PackageInfo> mActivePackagesCache;
51
52 ApexManager() {
53 mApexService = IApexService.Stub.asInterface(
54 ServiceManager.getService("apexservice"));
55 mActivePackagesCache = populateActivePackagesCache();
56 }
57
58 @NonNull
59 private Map<String, PackageInfo> populateActivePackagesCache() {
60 try {
61 List<PackageInfo> list = new ArrayList<>();
62 final ApexInfo[] activePkgs = mApexService.getActivePackages();
63 for (ApexInfo ai : activePkgs) {
64 // If the device is using flattened APEX, don't report any APEX
65 // packages since they won't be managed or updated by PackageManager.
66 if ((new File(ai.packagePath)).isDirectory()) {
67 break;
68 }
69 try {
70 list.add(PackageParser.generatePackageInfoFromApex(
71 new File(ai.packagePath), true /* collect certs */));
72 } catch (PackageParserException pe) {
73 throw new IllegalStateException("Unable to parse: " + ai, pe);
74 }
75 }
76 return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity()));
77 } catch (RemoteException re) {
78 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
79 throw new RuntimeException(re);
80 }
81 }
82
83 /**
84 * Retrieves information about an active APEX package.
85 *
86 * @param packageName the package name to look for. Note that this is the package name reported
87 * in the APK container manifest (i.e. AndroidManifest.xml), which might
88 * differ from the one reported in the APEX manifest (i.e.
89 * apex_manifest.json).
90 * @return a PackageInfo object with the information about the package, or null if the package
91 * is not found.
92 */
93 @Nullable PackageInfo getActivePackage(String packageName) {
94 return mActivePackagesCache.get(packageName);
95 }
96
97 /**
98 * Retrieves information about all active APEX packages.
99 *
100 * @return a Collection of PackageInfo object, each one containing information about a different
101 * active package.
102 */
103 Collection<PackageInfo> getActivePackages() {
104 return mActivePackagesCache.values();
105 }
106
107 /**
108 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
109 * track the different states of a session.
110 *
111 * @param sessionId the identifier of the session.
112 * @return an ApexSessionInfo object, or null if the session is not known.
113 */
114 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
115 try {
116 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
117 if (apexSessionInfo.isUnknown) {
118 return null;
119 }
120 return apexSessionInfo;
121 } catch (RemoteException re) {
122 Slog.e(TAG, "Unable to contact apexservice", re);
123 throw new RuntimeException(re);
124 }
125 }
126
127 /**
128 * Submit a staged session to apex service. This causes the apex service to perform some initial
129 * verification and accept or reject the session. Submitting a session successfully is not
130 * enough for it to be activated at the next boot, the caller needs to call
131 * {@link #markStagedSessionReady(int)}.
132 *
133 * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted.
134 * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain
135 * an array of identifiers of all the child sessions. Otherwise it should
136 * be an empty array.
137 * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller
138 * and will be filled with a list of {@link ApexInfo} objects, each of which
139 * contains metadata about one of the packages being submitted as part of
140 * the session.
141 * @return whether the submission of the session was successful.
142 */
143 boolean submitStagedSession(
144 int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) {
145 try {
146 return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList);
147 } catch (RemoteException re) {
148 Slog.e(TAG, "Unable to contact apexservice", re);
149 throw new RuntimeException(re);
150 }
151 }
152
153 /**
Dario Frenia0e3dda2019-02-18 20:58:54 +0000154 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
Dario Freni2e8dffc2019-02-06 14:55:16 +0000155 * applied at next reboot.
156 *
157 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
158 * @return true upon success, false if the session is unknown.
159 */
160 boolean markStagedSessionReady(int sessionId) {
161 try {
162 return mApexService.markStagedSessionReady(sessionId);
163 } catch (RemoteException re) {
164 Slog.e(TAG, "Unable to contact apexservice", re);
165 throw new RuntimeException(re);
166 }
167 }
168
169 /**
170 * Dumps various state information to the provided {@link PrintWriter} object.
171 *
172 * @param pw the {@link PrintWriter} object to send information to.
173 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
174 * information about that specific package will be dumped.
175 */
176 void dump(PrintWriter pw, @Nullable String packageName) {
177 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
178 ipw.println();
179 ipw.println("Active APEX packages:");
180 ipw.increaseIndent();
181 try {
182 populateActivePackagesCache();
183 for (PackageInfo pi : mActivePackagesCache.values()) {
184 if (packageName != null && !packageName.equals(pi.packageName)) {
185 continue;
186 }
187 ipw.println(pi.packageName);
188 ipw.increaseIndent();
189 ipw.println("Version: " + pi.versionCode);
190 ipw.println("Path: " + pi.applicationInfo.sourceDir);
191 ipw.decreaseIndent();
192 }
193 ipw.decreaseIndent();
194 ipw.println();
195 ipw.println("APEX session state:");
196 ipw.increaseIndent();
197 final ApexSessionInfo[] sessions = mApexService.getSessions();
198 for (ApexSessionInfo si : sessions) {
199 ipw.println("Session ID: " + Integer.toString(si.sessionId));
200 ipw.increaseIndent();
201 if (si.isUnknown) {
202 ipw.println("State: UNKNOWN");
203 } else if (si.isVerified) {
204 ipw.println("State: VERIFIED");
205 } else if (si.isStaged) {
206 ipw.println("State: STAGED");
207 } else if (si.isActivated) {
208 ipw.println("State: ACTIVATED");
209 } else if (si.isActivationPendingRetry) {
210 ipw.println("State: ACTIVATION PENDING RETRY");
211 } else if (si.isActivationFailed) {
212 ipw.println("State: ACTIVATION FAILED");
213 }
214 ipw.decreaseIndent();
215 }
216 ipw.decreaseIndent();
217 } catch (RemoteException e) {
218 ipw.println("Couldn't communicate with apexd.");
219 }
220 }
221}