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