blob: 2dbb34d888e5f74b28dc68b34c9da6a96c9befac [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;
20import android.content.pm.PackageInfo;
21import android.content.pm.PackageManager;
22import android.content.pm.dex.ArtManager;
23import android.os.Binder;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080024import android.os.Environment;
Calin Juravle45f8b292017-11-07 18:49:43 -080025import android.os.Handler;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080026import android.os.ParcelFileDescriptor;
Calin Juravle45f8b292017-11-07 18:49:43 -080027import android.os.RemoteException;
28import android.content.pm.IPackageManager;
29import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
30import android.os.SystemProperties;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080031import android.os.UserHandle;
Calin Juravle45f8b292017-11-07 18:49:43 -080032import android.util.Slog;
33
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080034import com.android.internal.annotations.GuardedBy;
Calin Juravle45f8b292017-11-07 18:49:43 -080035import com.android.internal.os.BackgroundThread;
36import com.android.internal.util.Preconditions;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080037import com.android.server.pm.Installer;
38import com.android.server.pm.Installer.InstallerException;
39import java.io.File;
40import java.io.FileNotFoundException;
Calin Juravle45f8b292017-11-07 18:49:43 -080041
42/**
43 * A system service that provides access to runtime and compiler artifacts.
44 *
45 * This service is not accessed by users directly, instead one uses an instance of
46 * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
47 * <p/>
48 * {@code context().getPackageManager().getArtManager();}
49 * <p class="note">
50 * Note: Accessing runtime artifacts may require extra permissions. For example querying the
51 * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
52 * which is a system-level permission that will not be granted to normal apps.
53 */
54public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
55 private static final String TAG = "ArtManagerService";
56
57 private static boolean DEBUG = false;
58 private static boolean DEBUG_IGNORE_PERMISSIONS = false;
59
60 private final IPackageManager mPackageManager;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080061 private final Object mInstallLock;
62 @GuardedBy("mInstallLock")
63 private final Installer mInstaller;
64
Calin Juravle45f8b292017-11-07 18:49:43 -080065 private final Handler mHandler;
66
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080067 public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
Calin Juravle45f8b292017-11-07 18:49:43 -080068 mPackageManager = pm;
Calin Juravlefd9f8ae2017-11-29 18:26:55 -080069 mInstaller = installer;
70 mInstallLock = installLock;
Calin Juravle45f8b292017-11-07 18:49:43 -080071 mHandler = new Handler(BackgroundThread.getHandler().getLooper());
72 }
73
74 @Override
75 public void snapshotRuntimeProfile(String packageName, String codePath,
76 ISnapshotRuntimeProfileCallback callback) {
77 // Sanity checks on the arguments.
78 Preconditions.checkStringNotEmpty(packageName);
79 Preconditions.checkStringNotEmpty(codePath);
80 Preconditions.checkNotNull(callback);
81
82 // Verify that the caller has the right permissions.
83 checkReadRuntimeProfilePermission();
84
85 if (DEBUG) {
86 Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
87 }
88
89 PackageInfo info = null;
90 try {
91 // Note that we use the default user 0 to retrieve the package info.
92 // This doesn't really matter because for user 0 we always get a package back (even if
93 // it's not installed for the user 0). It is ok because we only care about the code
94 // paths and not if the package is enabled or not for the user.
95
96 // TODO(calin): consider adding an API to PMS which can retrieve the
97 // PackageParser.Package.
98 info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
99 } catch (RemoteException ignored) {
100 // Should not happen.
101 }
102 if (info == null) {
103 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
104 return;
105 }
106
107 boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
108 String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
109 if (!pathFound && (splitCodePaths != null)) {
110 for (String path : splitCodePaths) {
111 if (path.equals(codePath)) {
112 pathFound = true;
113 break;
114 }
115 }
116 }
117 if (!pathFound) {
118 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
119 return;
120 }
121
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800122 // All good, create the profile snapshot.
123 createProfileSnapshot(packageName, codePath, callback, info);
124 // Destroy the snapshot, we no longer need it.
125 destroyProfileSnapshot(packageName, codePath);
126 }
127
128 private void createProfileSnapshot(String packageName, String codePath,
129 ISnapshotRuntimeProfileCallback callback, PackageInfo info) {
130 // Ask the installer to snapshot the profile.
131 synchronized (mInstallLock) {
132 try {
133 if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid),
134 packageName, codePath)) {
135 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
136 return;
137 }
138 } catch (InstallerException e) {
139 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
140 return;
141 }
142 }
143
144 // Open the snapshot and invoke the callback.
145 File snapshotProfile = Environment.getProfileSnapshotPath(packageName, codePath);
146 ParcelFileDescriptor fd;
147 try {
148 fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
149 postSuccess(packageName, fd, callback);
150 } catch (FileNotFoundException e) {
151 Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + codePath, e);
152 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
153 }
154 }
155
156 private void destroyProfileSnapshot(String packageName, String codePath) {
157 if (DEBUG) {
158 Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + codePath);
159 }
160
161 synchronized (mInstallLock) {
162 try {
163 mInstaller.destroyProfileSnapshot(packageName, codePath);
164 } catch (InstallerException e) {
165 Slog.e(TAG, "Failed to destroy profile snapshot for " +
166 packageName + ":" + codePath, e);
167 }
168 }
Calin Juravle45f8b292017-11-07 18:49:43 -0800169 }
170
171 @Override
172 public boolean isRuntimeProfilingEnabled() {
173 // Verify that the caller has the right permissions.
174 checkReadRuntimeProfilePermission();
175
176 return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
177 }
178
179 /**
180 * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
181 * on the internal {@code mHandler}.
182 */
183 private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
184 int errCode) {
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800185 if (DEBUG) {
186 Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
187 errCode);
188 }
Calin Juravle45f8b292017-11-07 18:49:43 -0800189 mHandler.post(() -> {
190 try {
191 callback.onError(errCode);
192 } catch (RemoteException e) {
193 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
194 }
195 });
196 }
197
Calin Juravlefd9f8ae2017-11-29 18:26:55 -0800198 private void postSuccess(String packageName, ParcelFileDescriptor fd,
199 ISnapshotRuntimeProfileCallback callback) {
200 if (DEBUG) {
201 Slog.d(TAG, "Successfully snapshot profile for " + packageName);
202 }
203 mHandler.post(() -> {
204 try {
205 callback.onSuccess(fd);
206 } catch (RemoteException e) {
207 Slog.w(TAG,
208 "Failed to call onSuccess after profile snapshot for " + packageName, e);
209 }
210 });
211 }
212
Calin Juravle45f8b292017-11-07 18:49:43 -0800213 /**
214 * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
215 * If not, it throws a {@link SecurityException}.
216 */
217 private void checkReadRuntimeProfilePermission() {
218 if (DEBUG_IGNORE_PERMISSIONS) {
219 return;
220 }
221 try {
222 int result = mPackageManager.checkUidPermission(
223 Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
224 if (result != PackageManager.PERMISSION_GRANTED) {
225 throw new SecurityException("You need "
226 + Manifest.permission.READ_RUNTIME_PROFILES
227 + " permission to snapshot profiles.");
228 }
229 } catch (RemoteException e) {
230 // Should not happen.
231 }
232 }
233}