ArtManagerService.java revision fd9f8ae973122a50f336e38c386ecbc0095d8adc
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.content.pm.PackageInfo; 21import android.content.pm.PackageManager; 22import android.content.pm.dex.ArtManager; 23import android.os.Binder; 24import android.os.Environment; 25import android.os.Handler; 26import android.os.ParcelFileDescriptor; 27import android.os.RemoteException; 28import android.content.pm.IPackageManager; 29import android.content.pm.dex.ISnapshotRuntimeProfileCallback; 30import android.os.SystemProperties; 31import android.os.UserHandle; 32import android.util.Slog; 33 34import com.android.internal.annotations.GuardedBy; 35import com.android.internal.os.BackgroundThread; 36import com.android.internal.util.Preconditions; 37import com.android.server.pm.Installer; 38import com.android.server.pm.Installer.InstallerException; 39import java.io.File; 40import java.io.FileNotFoundException; 41 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; 61 private final Object mInstallLock; 62 @GuardedBy("mInstallLock") 63 private final Installer mInstaller; 64 65 private final Handler mHandler; 66 67 public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) { 68 mPackageManager = pm; 69 mInstaller = installer; 70 mInstallLock = installLock; 71 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 122 // 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 } 169 } 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) { 185 if (DEBUG) { 186 Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " + 187 errCode); 188 } 189 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 198 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 213 /** 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} 234