blob: e29027288e9bdc74a50ce41ce38cc55b7afe8417 [file] [log] [blame]
Calin Juravle45f8b292017-11-07 18:49:43 -08001/*
2 * Copyright (C) 2017 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
15 */
16
17package com.android.server.pm.dex;
18
19import android.Manifest;
Calin Juravle3621be72018-01-18 15:17:29 -080020import android.annotation.UserIdInt;
21import android.content.pm.ApplicationInfo;
Calin Juravle45f8b292017-11-07 18:49:43 -080022import android.content.pm.PackageInfo;
23import android.content.pm.PackageManager;
Calin Juravle3621be72018-01-18 15:17:29 -080024import android.content.pm.PackageParser;
Calin Juravle45f8b292017-11-07 18:49:43 -080025import android.content.pm.dex.ArtManager;
Calin Juravlefcbb74a2018-01-21 21:39:18 -080026import android.content.pm.dex.ArtManager.ProfileType;
Calin Juravle3621be72018-01-18 15:17:29 -080027import android.content.pm.dex.DexMetadataHelper;
Calin Juravle45f8b292017-11-07 18:49:43 -080028import android.os.Binder;
Calin Juravlefcbb74a2018-01-21 21:39:18 -080029import android.os.Build;
Calin Juravle45f8b292017-11-07 18:49:43 -080030import android.os.Handler;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080031import android.os.ParcelFileDescriptor;
Calin Juravle45f8b292017-11-07 18:49:43 -080032import android.os.RemoteException;
33import android.content.pm.IPackageManager;
34import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
35import android.os.SystemProperties;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080036import android.os.UserHandle;
Calin Juravlefcbb74a2018-01-21 21:39:18 -080037import android.system.Os;
Calin Juravle3621be72018-01-18 15:17:29 -080038import android.util.ArrayMap;
Calin Juravle45f8b292017-11-07 18:49:43 -080039import android.util.Slog;
40
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080041import com.android.internal.annotations.GuardedBy;
Calin Juravle45f8b292017-11-07 18:49:43 -080042import com.android.internal.os.BackgroundThread;
Calin Juravle3621be72018-01-18 15:17:29 -080043import com.android.internal.util.ArrayUtils;
Calin Juravle45f8b292017-11-07 18:49:43 -080044import com.android.internal.util.Preconditions;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080045import com.android.server.pm.Installer;
46import com.android.server.pm.Installer.InstallerException;
47import java.io.File;
48import java.io.FileNotFoundException;
Calin Juravlefcbb74a2018-01-21 21:39:18 -080049import libcore.io.IoUtils;
50import libcore.util.NonNull;
51import libcore.util.Nullable;
Calin Juravle45f8b292017-11-07 18:49:43 -080052
53/**
54 * A system service that provides access to runtime and compiler artifacts.
55 *
56 * This service is not accessed by users directly, instead one uses an instance of
57 * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
58 * <p/>
59 * {@code context().getPackageManager().getArtManager();}
60 * <p class="note">
61 * Note: Accessing runtime artifacts may require extra permissions. For example querying the
62 * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
63 * which is a system-level permission that will not be granted to normal apps.
64 */
65public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
66 private static final String TAG = "ArtManagerService";
67
68 private static boolean DEBUG = false;
69 private static boolean DEBUG_IGNORE_PERMISSIONS = false;
70
Calin Juravlefcbb74a2018-01-21 21:39:18 -080071 // Package name used to create the profile directory layout when
72 // taking a snapshot of the boot image profile.
73 private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android";
74 // Profile name used for the boot image profile.
75 private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof";
76
Calin Juravle45f8b292017-11-07 18:49:43 -080077 private final IPackageManager mPackageManager;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080078 private final Object mInstallLock;
79 @GuardedBy("mInstallLock")
80 private final Installer mInstaller;
81
Calin Juravle45f8b292017-11-07 18:49:43 -080082 private final Handler mHandler;
83
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080084 public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
Calin Juravle45f8b292017-11-07 18:49:43 -080085 mPackageManager = pm;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080086 mInstaller = installer;
87 mInstallLock = installLock;
Calin Juravle45f8b292017-11-07 18:49:43 -080088 mHandler = new Handler(BackgroundThread.getHandler().getLooper());
89 }
90
91 @Override
Calin Juravlefcbb74a2018-01-21 21:39:18 -080092 public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
93 @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) {
Calin Juravle45f8b292017-11-07 18:49:43 -080094 // Sanity checks on the arguments.
Calin Juravle45f8b292017-11-07 18:49:43 -080095 Preconditions.checkNotNull(callback);
96
Calin Juravlefcbb74a2018-01-21 21:39:18 -080097 boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
98 if (!bootImageProfile) {
99 Preconditions.checkStringNotEmpty(codePath);
100 Preconditions.checkStringNotEmpty(packageName);
101 }
102
103 // Verify that the caller has the right permissions and that the runtime profiling is
104 // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission.
105 if (!isRuntimeProfilingEnabled(profileType)) {
106 throw new IllegalStateException("Runtime profiling is not enabled for " + profileType);
107 }
Calin Juravle45f8b292017-11-07 18:49:43 -0800108
109 if (DEBUG) {
110 Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
111 }
112
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800113 if (bootImageProfile) {
114 snapshotBootImageProfile(callback);
115 } else {
116 snapshotAppProfile(packageName, codePath, callback);
117 }
118 }
119
120 private void snapshotAppProfile(String packageName, String codePath,
121 ISnapshotRuntimeProfileCallback callback) {
Calin Juravle45f8b292017-11-07 18:49:43 -0800122 PackageInfo info = null;
123 try {
124 // Note that we use the default user 0 to retrieve the package info.
125 // This doesn't really matter because for user 0 we always get a package back (even if
126 // it's not installed for the user 0). It is ok because we only care about the code
127 // paths and not if the package is enabled or not for the user.
128
129 // TODO(calin): consider adding an API to PMS which can retrieve the
130 // PackageParser.Package.
131 info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
132 } catch (RemoteException ignored) {
133 // Should not happen.
134 }
135 if (info == null) {
136 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
137 return;
138 }
139
140 boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
Calin Juravle6ae39fc2018-01-19 20:32:47 -0800141 String splitName = null;
Calin Juravle45f8b292017-11-07 18:49:43 -0800142 String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
143 if (!pathFound && (splitCodePaths != null)) {
Calin Juravle6ae39fc2018-01-19 20:32:47 -0800144 for (int i = splitCodePaths.length - 1; i >= 0; i--) {
145 if (splitCodePaths[i].equals(codePath)) {
Calin Juravle45f8b292017-11-07 18:49:43 -0800146 pathFound = true;
Calin Juravle6ae39fc2018-01-19 20:32:47 -0800147 splitName = info.applicationInfo.splitNames[i];
Calin Juravle45f8b292017-11-07 18:49:43 -0800148 break;
149 }
150 }
151 }
152 if (!pathFound) {
153 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
154 return;
155 }
156
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800157 // All good, create the profile snapshot.
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800158 int appId = UserHandle.getAppId(info.applicationInfo.uid);
159 if (appId < 0) {
160 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
161 Slog.wtf(TAG, "AppId is -1 for package: " + packageName);
162 return;
163 }
164
165 createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath,
166 appId, callback);
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800167 // Destroy the snapshot, we no longer need it.
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800168 destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName));
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800169 }
170
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800171 private void createProfileSnapshot(String packageName, String profileName, String classpath,
172 int appId, ISnapshotRuntimeProfileCallback callback) {
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800173 // Ask the installer to snapshot the profile.
174 synchronized (mInstallLock) {
175 try {
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800176 if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) {
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800177 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
178 return;
179 }
180 } catch (InstallerException e) {
181 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
182 return;
183 }
184 }
185
186 // Open the snapshot and invoke the callback.
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800187 File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName);
188
189 ParcelFileDescriptor fd = null;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800190 try {
191 fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
192 postSuccess(packageName, fd, callback);
193 } catch (FileNotFoundException e) {
Calin Juravle6ae39fc2018-01-19 20:32:47 -0800194 Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
195 + snapshotProfile, e);
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800196 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800197 } finally {
198 IoUtils.closeQuietly(fd);
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800199 }
200 }
201
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800202 private void destroyProfileSnapshot(String packageName, String profileName) {
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800203 if (DEBUG) {
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800204 Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName);
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800205 }
206
207 synchronized (mInstallLock) {
208 try {
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800209 mInstaller.destroyProfileSnapshot(packageName, profileName);
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800210 } catch (InstallerException e) {
211 Slog.e(TAG, "Failed to destroy profile snapshot for " +
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800212 packageName + ":" + profileName, e);
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800213 }
214 }
Calin Juravle45f8b292017-11-07 18:49:43 -0800215 }
216
217 @Override
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800218 public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
Calin Juravle45f8b292017-11-07 18:49:43 -0800219 // Verify that the caller has the right permissions.
220 checkReadRuntimeProfilePermission();
221
Calin Juravlefcbb74a2018-01-21 21:39:18 -0800222 switch (profileType) {
223 case ArtManager.PROFILE_APPS :
224 return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
225 case ArtManager.PROFILE_BOOT_IMAGE:
226 return (Build.IS_USERDEBUG || Build.IS_ENG) &&
227 SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) &&
228 SystemProperties.getBoolean("dalvik.vm.profilebootimage", false);
229 default:
230 throw new IllegalArgumentException("Invalid profile type:" + profileType);
231 }
232 }
233
234 private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) {
235 // Combine the profiles for boot classpath and system server classpath.
236 // This avoids having yet another type of profiles and simplifies the processing.
237 String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"),
238 Os.getenv("SYSTEMSERVERCLASSPATH"));
239
240 // Create the snapshot.
241 createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath,
242 /*appId*/ -1, callback);
243 // Destroy the snapshot, we no longer need it.
244 destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME);
Calin Juravle45f8b292017-11-07 18:49:43 -0800245 }
246
247 /**
248 * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
249 * on the internal {@code mHandler}.
250 */
251 private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
252 int errCode) {
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800253 if (DEBUG) {
254 Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
255 errCode);
256 }
Calin Juravle45f8b292017-11-07 18:49:43 -0800257 mHandler.post(() -> {
258 try {
259 callback.onError(errCode);
260 } catch (RemoteException e) {
261 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
262 }
263 });
264 }
265
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800266 private void postSuccess(String packageName, ParcelFileDescriptor fd,
267 ISnapshotRuntimeProfileCallback callback) {
268 if (DEBUG) {
269 Slog.d(TAG, "Successfully snapshot profile for " + packageName);
270 }
271 mHandler.post(() -> {
272 try {
273 callback.onSuccess(fd);
274 } catch (RemoteException e) {
275 Slog.w(TAG,
276 "Failed to call onSuccess after profile snapshot for " + packageName, e);
277 }
278 });
279 }
280
Calin Juravle45f8b292017-11-07 18:49:43 -0800281 /**
282 * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
283 * If not, it throws a {@link SecurityException}.
284 */
285 private void checkReadRuntimeProfilePermission() {
286 if (DEBUG_IGNORE_PERMISSIONS) {
287 return;
288 }
289 try {
290 int result = mPackageManager.checkUidPermission(
291 Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
292 if (result != PackageManager.PERMISSION_GRANTED) {
293 throw new SecurityException("You need "
294 + Manifest.permission.READ_RUNTIME_PROFILES
295 + " permission to snapshot profiles.");
296 }
297 } catch (RemoteException e) {
298 // Should not happen.
299 }
300 }
Calin Juravle3621be72018-01-18 15:17:29 -0800301
302 /**
303 * Prepare the application profiles.
304 * For all code paths:
305 * - create the current primary profile to save time at app startup time.
306 * - copy the profiles from the associated dex metadata file to the reference profile.
307 */
308 public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) {
309 final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
Calin Juravle5bbe26e2018-01-24 14:43:24 -0800310 if (user < 0) {
311 Slog.wtf(TAG, "Invalid user id: " + user);
312 return;
313 }
314 if (appId < 0) {
315 Slog.wtf(TAG, "Invalid app id: " + appId);
316 return;
317 }
Calin Juravle3621be72018-01-18 15:17:29 -0800318 try {
319 ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);
320 for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
321 String codePath = codePathsProfileNames.keyAt(i);
322 String profileName = codePathsProfileNames.valueAt(i);
323 File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
324 String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
325 synchronized (mInstaller) {
326 boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
327 profileName, codePath, dexMetadataPath);
328 if (!result) {
329 Slog.e(TAG, "Failed to prepare profile for " +
330 pkg.packageName + ":" + codePath);
331 }
332 }
333 }
334 } catch (InstallerException e) {
335 Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);
336 }
337 }
338
339 /**
Calin Juravle5bbe26e2018-01-24 14:43:24 -0800340 * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
341 */
342 public void prepareAppProfiles(PackageParser.Package pkg, int[] user) {
343 for (int i = 0; i < user.length; i++) {
344 prepareAppProfiles(pkg, user[i]);
345 }
346 }
347
348 /**
Calin Juravle6ae39fc2018-01-19 20:32:47 -0800349 * Clear the profiles for the given package.
350 */
351 public void clearAppProfiles(PackageParser.Package pkg) {
352 try {
353 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
354 for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
355 String profileName = packageProfileNames.valueAt(i);
356 mInstaller.clearAppProfiles(pkg.packageName, profileName);
357 }
358 } catch (InstallerException e) {
359 Slog.w(TAG, String.valueOf(e));
360 }
361 }
362
363 /**
364 * Dumps the profiles for the given package.
365 */
366 public void dumpProfiles(PackageParser.Package pkg) {
367 final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
368 try {
369 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
370 for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
371 String codePath = packageProfileNames.keyAt(i);
372 String profileName = packageProfileNames.valueAt(i);
373 synchronized (mInstallLock) {
374 mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath);
375 }
376 }
377 } catch (InstallerException e) {
378 Slog.w(TAG, "Failed to dump profiles", e);
379 }
380 }
381
382 /**
Calin Juravle3621be72018-01-18 15:17:29 -0800383 * Build the profiles names for all the package code paths (excluding resource only paths).
384 * Return the map [code path -> profile name].
385 */
386 private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) {
387 ArrayMap<String, String> result = new ArrayMap<>();
388 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
389 result.put(pkg.baseCodePath, ArtManager.getProfileName(null));
390 }
391 if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
392 for (int i = 0; i < pkg.splitCodePaths.length; i++) {
393 if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
394 result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i]));
395 }
396 }
397 }
398 return result;
399 }
Calin Juravle45f8b292017-11-07 18:49:43 -0800400}