ArtManagerService.java revision ea6c0ffb4a276210b6d971c87a15a7484446d3df
1/* 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.annotation.UserIdInt; 21import android.content.pm.ApplicationInfo; 22import android.content.pm.PackageInfo; 23import android.content.pm.PackageManager; 24import android.content.pm.PackageParser; 25import android.content.pm.dex.ArtManager; 26import android.content.pm.dex.DexMetadataHelper; 27import android.os.Binder; 28import android.os.Handler; 29import android.os.ParcelFileDescriptor; 30import android.os.RemoteException; 31import android.content.pm.IPackageManager; 32import android.content.pm.dex.ISnapshotRuntimeProfileCallback; 33import android.os.SystemProperties; 34import android.os.UserHandle; 35import android.util.ArrayMap; 36import android.util.Slog; 37 38import com.android.internal.annotations.GuardedBy; 39import com.android.internal.os.BackgroundThread; 40import com.android.internal.util.ArrayUtils; 41import com.android.internal.util.Preconditions; 42import com.android.server.pm.Installer; 43import com.android.server.pm.Installer.InstallerException; 44import java.io.File; 45import java.io.FileNotFoundException; 46 47/** 48 * A system service that provides access to runtime and compiler artifacts. 49 * 50 * This service is not accessed by users directly, instead one uses an instance of 51 * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows: 52 * <p/> 53 * {@code context().getPackageManager().getArtManager();} 54 * <p class="note"> 55 * Note: Accessing runtime artifacts may require extra permissions. For example querying the 56 * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES} 57 * which is a system-level permission that will not be granted to normal apps. 58 */ 59public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { 60 private static final String TAG = "ArtManagerService"; 61 62 private static boolean DEBUG = false; 63 private static boolean DEBUG_IGNORE_PERMISSIONS = false; 64 65 private final IPackageManager mPackageManager; 66 private final Object mInstallLock; 67 @GuardedBy("mInstallLock") 68 private final Installer mInstaller; 69 70 private final Handler mHandler; 71 72 public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) { 73 mPackageManager = pm; 74 mInstaller = installer; 75 mInstallLock = installLock; 76 mHandler = new Handler(BackgroundThread.getHandler().getLooper()); 77 } 78 79 @Override 80 public void snapshotRuntimeProfile(String packageName, String codePath, 81 ISnapshotRuntimeProfileCallback callback) { 82 // Sanity checks on the arguments. 83 Preconditions.checkStringNotEmpty(packageName); 84 Preconditions.checkStringNotEmpty(codePath); 85 Preconditions.checkNotNull(callback); 86 87 // Verify that the caller has the right permissions. 88 checkReadRuntimeProfilePermission(); 89 90 if (DEBUG) { 91 Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath); 92 } 93 94 PackageInfo info = null; 95 try { 96 // Note that we use the default user 0 to retrieve the package info. 97 // This doesn't really matter because for user 0 we always get a package back (even if 98 // it's not installed for the user 0). It is ok because we only care about the code 99 // paths and not if the package is enabled or not for the user. 100 101 // TODO(calin): consider adding an API to PMS which can retrieve the 102 // PackageParser.Package. 103 info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0); 104 } catch (RemoteException ignored) { 105 // Should not happen. 106 } 107 if (info == null) { 108 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND); 109 return; 110 } 111 112 boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath); 113 String splitName = null; 114 String[] splitCodePaths = info.applicationInfo.getSplitCodePaths(); 115 if (!pathFound && (splitCodePaths != null)) { 116 for (int i = splitCodePaths.length - 1; i >= 0; i--) { 117 if (splitCodePaths[i].equals(codePath)) { 118 pathFound = true; 119 splitName = info.applicationInfo.splitNames[i]; 120 break; 121 } 122 } 123 } 124 if (!pathFound) { 125 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND); 126 return; 127 } 128 129 // All good, create the profile snapshot. 130 createProfileSnapshot(packageName, splitName, callback, info); 131 // Destroy the snapshot, we no longer need it. 132 destroyProfileSnapshot(packageName, splitName); 133 } 134 135 private void createProfileSnapshot(String packageName, String splitName, 136 ISnapshotRuntimeProfileCallback callback, PackageInfo info) { 137 // Ask the installer to snapshot the profile. 138 synchronized (mInstallLock) { 139 try { 140 if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid), 141 packageName, ArtManager.getProfileName(splitName))) { 142 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 143 return; 144 } 145 } catch (InstallerException e) { 146 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 147 return; 148 } 149 } 150 151 // Open the snapshot and invoke the callback. 152 File snapshotProfile = ArtManager.getProfileSnapshotFile(packageName, splitName); 153 ParcelFileDescriptor fd; 154 try { 155 fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY); 156 postSuccess(packageName, fd, callback); 157 } catch (FileNotFoundException e) { 158 Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" 159 + snapshotProfile, e); 160 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 161 } 162 } 163 164 private void destroyProfileSnapshot(String packageName, String splitName) { 165 if (DEBUG) { 166 Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + splitName); 167 } 168 169 synchronized (mInstallLock) { 170 try { 171 mInstaller.destroyProfileSnapshot(packageName, 172 ArtManager.getProfileName(splitName)); 173 } catch (InstallerException e) { 174 Slog.e(TAG, "Failed to destroy profile snapshot for " + 175 packageName + ":" + splitName, e); 176 } 177 } 178 } 179 180 @Override 181 public boolean isRuntimeProfilingEnabled() { 182 // Verify that the caller has the right permissions. 183 checkReadRuntimeProfilePermission(); 184 185 return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); 186 } 187 188 /** 189 * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message 190 * on the internal {@code mHandler}. 191 */ 192 private void postError(ISnapshotRuntimeProfileCallback callback, String packageName, 193 int errCode) { 194 if (DEBUG) { 195 Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " + 196 errCode); 197 } 198 mHandler.post(() -> { 199 try { 200 callback.onError(errCode); 201 } catch (RemoteException e) { 202 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e); 203 } 204 }); 205 } 206 207 private void postSuccess(String packageName, ParcelFileDescriptor fd, 208 ISnapshotRuntimeProfileCallback callback) { 209 if (DEBUG) { 210 Slog.d(TAG, "Successfully snapshot profile for " + packageName); 211 } 212 mHandler.post(() -> { 213 try { 214 callback.onSuccess(fd); 215 } catch (RemoteException e) { 216 Slog.w(TAG, 217 "Failed to call onSuccess after profile snapshot for " + packageName, e); 218 } 219 }); 220 } 221 222 /** 223 * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}. 224 * If not, it throws a {@link SecurityException}. 225 */ 226 private void checkReadRuntimeProfilePermission() { 227 if (DEBUG_IGNORE_PERMISSIONS) { 228 return; 229 } 230 try { 231 int result = mPackageManager.checkUidPermission( 232 Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid()); 233 if (result != PackageManager.PERMISSION_GRANTED) { 234 throw new SecurityException("You need " 235 + Manifest.permission.READ_RUNTIME_PROFILES 236 + " permission to snapshot profiles."); 237 } 238 } catch (RemoteException e) { 239 // Should not happen. 240 } 241 } 242 243 /** 244 * Prepare the application profiles. 245 * For all code paths: 246 * - create the current primary profile to save time at app startup time. 247 * - copy the profiles from the associated dex metadata file to the reference profile. 248 */ 249 public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) { 250 final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); 251 if (user < 0) { 252 Slog.wtf(TAG, "Invalid user id: " + user); 253 return; 254 } 255 if (appId < 0) { 256 Slog.wtf(TAG, "Invalid app id: " + appId); 257 return; 258 } 259 try { 260 ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg); 261 for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) { 262 String codePath = codePathsProfileNames.keyAt(i); 263 String profileName = codePathsProfileNames.valueAt(i); 264 File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath)); 265 String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath(); 266 synchronized (mInstaller) { 267 boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId, 268 profileName, codePath, dexMetadataPath); 269 if (!result) { 270 Slog.e(TAG, "Failed to prepare profile for " + 271 pkg.packageName + ":" + codePath); 272 } 273 } 274 } 275 } catch (InstallerException e) { 276 Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e); 277 } 278 } 279 280 /** 281 * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}. 282 */ 283 public void prepareAppProfiles(PackageParser.Package pkg, int[] user) { 284 for (int i = 0; i < user.length; i++) { 285 prepareAppProfiles(pkg, user[i]); 286 } 287 } 288 289 /** 290 * Clear the profiles for the given package. 291 */ 292 public void clearAppProfiles(PackageParser.Package pkg) { 293 try { 294 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); 295 for (int i = packageProfileNames.size() - 1; i >= 0; i--) { 296 String profileName = packageProfileNames.valueAt(i); 297 mInstaller.clearAppProfiles(pkg.packageName, profileName); 298 } 299 } catch (InstallerException e) { 300 Slog.w(TAG, String.valueOf(e)); 301 } 302 } 303 304 /** 305 * Dumps the profiles for the given package. 306 */ 307 public void dumpProfiles(PackageParser.Package pkg) { 308 final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); 309 try { 310 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); 311 for (int i = packageProfileNames.size() - 1; i >= 0; i--) { 312 String codePath = packageProfileNames.keyAt(i); 313 String profileName = packageProfileNames.valueAt(i); 314 synchronized (mInstallLock) { 315 mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath); 316 } 317 } 318 } catch (InstallerException e) { 319 Slog.w(TAG, "Failed to dump profiles", e); 320 } 321 } 322 323 /** 324 * Build the profiles names for all the package code paths (excluding resource only paths). 325 * Return the map [code path -> profile name]. 326 */ 327 private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) { 328 ArrayMap<String, String> result = new ArrayMap<>(); 329 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { 330 result.put(pkg.baseCodePath, ArtManager.getProfileName(null)); 331 } 332 if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { 333 for (int i = 0; i < pkg.splitCodePaths.length; i++) { 334 if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { 335 result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i])); 336 } 337 } 338 } 339 return result; 340 } 341} 342