ArtManagerService.java revision 7fc0f633bc11c9fd89eb4bd3581ea2e85eaa31c1
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.IPackageManager; 23import android.content.pm.PackageInfo; 24import android.content.pm.PackageManager; 25import android.content.pm.PackageParser; 26import android.content.pm.dex.ArtManager; 27import android.content.pm.dex.ArtManager.ProfileType; 28import android.content.pm.dex.ArtManagerInternal; 29import android.content.pm.dex.DexMetadataHelper; 30import android.content.pm.dex.ISnapshotRuntimeProfileCallback; 31import android.content.pm.dex.PackageOptimizationInfo; 32import android.os.Binder; 33import android.os.Build; 34import android.os.Handler; 35import android.os.ParcelFileDescriptor; 36import android.os.RemoteException; 37import android.os.SystemProperties; 38import android.os.UserHandle; 39import android.system.Os; 40import android.util.ArrayMap; 41import android.util.Slog; 42import com.android.internal.annotations.GuardedBy; 43import com.android.internal.os.BackgroundThread; 44import com.android.internal.util.ArrayUtils; 45import com.android.internal.util.Preconditions; 46import com.android.server.LocalServices; 47import com.android.server.pm.Installer; 48import com.android.server.pm.Installer.InstallerException; 49import com.android.server.pm.PackageManagerServiceCompilerMapping; 50import dalvik.system.DexFile; 51import dalvik.system.VMRuntime; 52import java.io.File; 53import java.io.FileNotFoundException; 54import libcore.io.IoUtils; 55import libcore.util.NonNull; 56import libcore.util.Nullable; 57 58/** 59 * A system service that provides access to runtime and compiler artifacts. 60 * 61 * This service is not accessed by users directly, instead one uses an instance of 62 * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows: 63 * <p/> 64 * {@code context().getPackageManager().getArtManager();} 65 * <p class="note"> 66 * Note: Accessing runtime artifacts may require extra permissions. For example querying the 67 * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES} 68 * which is a system-level permission that will not be granted to normal apps. 69 */ 70public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { 71 private static final String TAG = "ArtManagerService"; 72 73 private static boolean DEBUG = false; 74 private static boolean DEBUG_IGNORE_PERMISSIONS = false; 75 76 // Package name used to create the profile directory layout when 77 // taking a snapshot of the boot image profile. 78 private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android"; 79 // Profile name used for the boot image profile. 80 private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof"; 81 82 private final IPackageManager mPackageManager; 83 private final Object mInstallLock; 84 @GuardedBy("mInstallLock") 85 private final Installer mInstaller; 86 87 private final Handler mHandler; 88 89 static { 90 verifyTronLoggingConstants(); 91 } 92 93 public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) { 94 mPackageManager = pm; 95 mInstaller = installer; 96 mInstallLock = installLock; 97 mHandler = new Handler(BackgroundThread.getHandler().getLooper()); 98 99 LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl()); 100 } 101 102 @Override 103 public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, 104 @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) { 105 // Sanity checks on the arguments. 106 Preconditions.checkNotNull(callback); 107 108 boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE; 109 if (!bootImageProfile) { 110 Preconditions.checkStringNotEmpty(codePath); 111 Preconditions.checkStringNotEmpty(packageName); 112 } 113 114 // Verify that the caller has the right permissions and that the runtime profiling is 115 // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission. 116 if (!isRuntimeProfilingEnabled(profileType)) { 117 throw new IllegalStateException("Runtime profiling is not enabled for " + profileType); 118 } 119 120 if (DEBUG) { 121 Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath); 122 } 123 124 if (bootImageProfile) { 125 snapshotBootImageProfile(callback); 126 } else { 127 snapshotAppProfile(packageName, codePath, callback); 128 } 129 } 130 131 private void snapshotAppProfile(String packageName, String codePath, 132 ISnapshotRuntimeProfileCallback callback) { 133 PackageInfo info = null; 134 try { 135 // Note that we use the default user 0 to retrieve the package info. 136 // This doesn't really matter because for user 0 we always get a package back (even if 137 // it's not installed for the user 0). It is ok because we only care about the code 138 // paths and not if the package is enabled or not for the user. 139 140 // TODO(calin): consider adding an API to PMS which can retrieve the 141 // PackageParser.Package. 142 info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0); 143 } catch (RemoteException ignored) { 144 // Should not happen. 145 } 146 if (info == null) { 147 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND); 148 return; 149 } 150 151 boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath); 152 String splitName = null; 153 String[] splitCodePaths = info.applicationInfo.getSplitCodePaths(); 154 if (!pathFound && (splitCodePaths != null)) { 155 for (int i = splitCodePaths.length - 1; i >= 0; i--) { 156 if (splitCodePaths[i].equals(codePath)) { 157 pathFound = true; 158 splitName = info.applicationInfo.splitNames[i]; 159 break; 160 } 161 } 162 } 163 if (!pathFound) { 164 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND); 165 return; 166 } 167 168 // All good, create the profile snapshot. 169 int appId = UserHandle.getAppId(info.applicationInfo.uid); 170 if (appId < 0) { 171 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 172 Slog.wtf(TAG, "AppId is -1 for package: " + packageName); 173 return; 174 } 175 176 createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath, 177 appId, callback); 178 // Destroy the snapshot, we no longer need it. 179 destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName)); 180 } 181 182 private void createProfileSnapshot(String packageName, String profileName, String classpath, 183 int appId, ISnapshotRuntimeProfileCallback callback) { 184 // Ask the installer to snapshot the profile. 185 synchronized (mInstallLock) { 186 try { 187 if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) { 188 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 189 return; 190 } 191 } catch (InstallerException e) { 192 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 193 return; 194 } 195 } 196 197 // Open the snapshot and invoke the callback. 198 File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName); 199 200 ParcelFileDescriptor fd = null; 201 try { 202 fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY); 203 if (fd == null || !fd.getFileDescriptor().valid()) { 204 Slog.wtf(TAG, 205 "ParcelFileDescriptor.open returned an invalid descriptor for " 206 + packageName + ":" + snapshotProfile + ". isNull=" + (fd == null)); 207 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 208 } else { 209 postSuccess(packageName, fd, callback); 210 } 211 } catch (FileNotFoundException e) { 212 Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" 213 + snapshotProfile, e); 214 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 215 } 216 } 217 218 private void destroyProfileSnapshot(String packageName, String profileName) { 219 if (DEBUG) { 220 Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName); 221 } 222 223 synchronized (mInstallLock) { 224 try { 225 mInstaller.destroyProfileSnapshot(packageName, profileName); 226 } catch (InstallerException e) { 227 Slog.e(TAG, "Failed to destroy profile snapshot for " + 228 packageName + ":" + profileName, e); 229 } 230 } 231 } 232 233 @Override 234 public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { 235 // Verify that the caller has the right permissions. 236 checkReadRuntimeProfilePermission(); 237 238 switch (profileType) { 239 case ArtManager.PROFILE_APPS : 240 return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); 241 case ArtManager.PROFILE_BOOT_IMAGE: 242 return (Build.IS_USERDEBUG || Build.IS_ENG) && 243 SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) && 244 SystemProperties.getBoolean("dalvik.vm.profilebootimage", false); 245 default: 246 throw new IllegalArgumentException("Invalid profile type:" + profileType); 247 } 248 } 249 250 private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) { 251 // Combine the profiles for boot classpath and system server classpath. 252 // This avoids having yet another type of profiles and simplifies the processing. 253 String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"), 254 Os.getenv("SYSTEMSERVERCLASSPATH")); 255 256 // Create the snapshot. 257 createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath, 258 /*appId*/ -1, callback); 259 // Destroy the snapshot, we no longer need it. 260 destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME); 261 } 262 263 /** 264 * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message 265 * on the internal {@code mHandler}. 266 */ 267 private void postError(ISnapshotRuntimeProfileCallback callback, String packageName, 268 int errCode) { 269 if (DEBUG) { 270 Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " + 271 errCode); 272 } 273 mHandler.post(() -> { 274 try { 275 callback.onError(errCode); 276 } catch (Exception e) { 277 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e); 278 } 279 }); 280 } 281 282 private void postSuccess(String packageName, ParcelFileDescriptor fd, 283 ISnapshotRuntimeProfileCallback callback) { 284 if (DEBUG) { 285 Slog.d(TAG, "Successfully snapshot profile for " + packageName); 286 } 287 mHandler.post(() -> { 288 try { 289 // Double check that the descriptor is still valid. 290 // We've seen production issues (b/76028139) where this can turn invalid (there are 291 // suspicions around the finalizer behaviour). 292 if (fd.getFileDescriptor().valid()) { 293 callback.onSuccess(fd); 294 } else { 295 Slog.wtf(TAG, "The snapshot FD became invalid before posting the result for " 296 + packageName); 297 callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 298 } 299 } catch (Exception e) { 300 Slog.w(TAG, 301 "Failed to call onSuccess after profile snapshot for " + packageName, e); 302 } finally { 303 IoUtils.closeQuietly(fd); 304 } 305 }); 306 } 307 308 /** 309 * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}. 310 * If not, it throws a {@link SecurityException}. 311 */ 312 private void checkReadRuntimeProfilePermission() { 313 if (DEBUG_IGNORE_PERMISSIONS) { 314 return; 315 } 316 try { 317 int result = mPackageManager.checkUidPermission( 318 Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid()); 319 if (result != PackageManager.PERMISSION_GRANTED) { 320 throw new SecurityException("You need " 321 + Manifest.permission.READ_RUNTIME_PROFILES 322 + " permission to snapshot profiles."); 323 } 324 } catch (RemoteException e) { 325 // Should not happen. 326 } 327 } 328 329 /** 330 * Prepare the application profiles. 331 * For all code paths: 332 * - create the current primary profile to save time at app startup time. 333 * - copy the profiles from the associated dex metadata file to the reference profile. 334 */ 335 public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) { 336 final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); 337 if (user < 0) { 338 Slog.wtf(TAG, "Invalid user id: " + user); 339 return; 340 } 341 if (appId < 0) { 342 Slog.wtf(TAG, "Invalid app id: " + appId); 343 return; 344 } 345 try { 346 ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg); 347 for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) { 348 String codePath = codePathsProfileNames.keyAt(i); 349 String profileName = codePathsProfileNames.valueAt(i); 350 File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath)); 351 String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath(); 352 synchronized (mInstaller) { 353 boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId, 354 profileName, codePath, dexMetadataPath); 355 if (!result) { 356 Slog.e(TAG, "Failed to prepare profile for " + 357 pkg.packageName + ":" + codePath); 358 } 359 } 360 } 361 } catch (InstallerException e) { 362 Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e); 363 } 364 } 365 366 /** 367 * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}. 368 */ 369 public void prepareAppProfiles(PackageParser.Package pkg, int[] user) { 370 for (int i = 0; i < user.length; i++) { 371 prepareAppProfiles(pkg, user[i]); 372 } 373 } 374 375 /** 376 * Clear the profiles for the given package. 377 */ 378 public void clearAppProfiles(PackageParser.Package pkg) { 379 try { 380 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); 381 for (int i = packageProfileNames.size() - 1; i >= 0; i--) { 382 String profileName = packageProfileNames.valueAt(i); 383 mInstaller.clearAppProfiles(pkg.packageName, profileName); 384 } 385 } catch (InstallerException e) { 386 Slog.w(TAG, String.valueOf(e)); 387 } 388 } 389 390 /** 391 * Dumps the profiles for the given package. 392 */ 393 public void dumpProfiles(PackageParser.Package pkg) { 394 final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); 395 try { 396 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); 397 for (int i = packageProfileNames.size() - 1; i >= 0; i--) { 398 String codePath = packageProfileNames.keyAt(i); 399 String profileName = packageProfileNames.valueAt(i); 400 synchronized (mInstallLock) { 401 mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath); 402 } 403 } 404 } catch (InstallerException e) { 405 Slog.w(TAG, "Failed to dump profiles", e); 406 } 407 } 408 409 /** 410 * Build the profiles names for all the package code paths (excluding resource only paths). 411 * Return the map [code path -> profile name]. 412 */ 413 private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) { 414 ArrayMap<String, String> result = new ArrayMap<>(); 415 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { 416 result.put(pkg.baseCodePath, ArtManager.getProfileName(null)); 417 } 418 if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { 419 for (int i = 0; i < pkg.splitCodePaths.length; i++) { 420 if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { 421 result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i])); 422 } 423 } 424 } 425 return result; 426 } 427 428 // Constants used for logging compilation filter to TRON. 429 // DO NOT CHANGE existing values. 430 // 431 // NOTE: '-1' value is reserved for the case where we cannot produce a valid 432 // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the 433 // ActivityMetricsLoggers. 434 private static final int TRON_COMPILATION_FILTER_ERROR = 0; 435 private static final int TRON_COMPILATION_FILTER_UNKNOWN = 1; 436 private static final int TRON_COMPILATION_FILTER_ASSUMED_VERIFIED = 2; 437 private static final int TRON_COMPILATION_FILTER_EXTRACT = 3; 438 private static final int TRON_COMPILATION_FILTER_VERIFY = 4; 439 private static final int TRON_COMPILATION_FILTER_QUICKEN = 5; 440 private static final int TRON_COMPILATION_FILTER_SPACE_PROFILE = 6; 441 private static final int TRON_COMPILATION_FILTER_SPACE = 7; 442 private static final int TRON_COMPILATION_FILTER_SPEED_PROFILE = 8; 443 private static final int TRON_COMPILATION_FILTER_SPEED = 9; 444 private static final int TRON_COMPILATION_FILTER_EVERYTHING_PROFILE = 10; 445 private static final int TRON_COMPILATION_FILTER_EVERYTHING = 11; 446 private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK = 12; 447 private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = 13; 448 private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = 14; 449 450 // Constants used for logging compilation reason to TRON. 451 // DO NOT CHANGE existing values. 452 // 453 // NOTE: '-1' value is reserved for the case where we cannot produce a valid 454 // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the 455 // ActivityMetricsLoggers. 456 private static final int TRON_COMPILATION_REASON_ERROR = 0; 457 private static final int TRON_COMPILATION_REASON_UNKNOWN = 1; 458 private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2; 459 private static final int TRON_COMPILATION_REASON_BOOT = 3; 460 private static final int TRON_COMPILATION_REASON_INSTALL = 4; 461 private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5; 462 private static final int TRON_COMPILATION_REASON_AB_OTA = 6; 463 private static final int TRON_COMPILATION_REASON_INACTIVE = 7; 464 private static final int TRON_COMPILATION_REASON_SHARED = 8; 465 466 /** 467 * Convert the compilation reason to an int suitable to be logged to TRON. 468 */ 469 private static int getCompilationReasonTronValue(String compilationReason) { 470 switch (compilationReason) { 471 case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN; 472 case "error" : return TRON_COMPILATION_REASON_ERROR; 473 case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT; 474 case "boot" : return TRON_COMPILATION_REASON_BOOT; 475 case "install" : return TRON_COMPILATION_REASON_INSTALL; 476 case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT; 477 case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA; 478 case "inactive" : return TRON_COMPILATION_REASON_INACTIVE; 479 case "shared" : return TRON_COMPILATION_REASON_SHARED; 480 default: return TRON_COMPILATION_REASON_UNKNOWN; 481 } 482 } 483 484 /** 485 * Convert the compilation filter to an int suitable to be logged to TRON. 486 */ 487 private static int getCompilationFilterTronValue(String compilationFilter) { 488 switch (compilationFilter) { 489 case "error" : return TRON_COMPILATION_FILTER_ERROR; 490 case "unknown" : return TRON_COMPILATION_FILTER_UNKNOWN; 491 case "assume-verified" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED; 492 case "extract" : return TRON_COMPILATION_FILTER_EXTRACT; 493 case "verify" : return TRON_COMPILATION_FILTER_VERIFY; 494 case "quicken" : return TRON_COMPILATION_FILTER_QUICKEN; 495 case "space-profile" : return TRON_COMPILATION_FILTER_SPACE_PROFILE; 496 case "space" : return TRON_COMPILATION_FILTER_SPACE; 497 case "speed-profile" : return TRON_COMPILATION_FILTER_SPEED_PROFILE; 498 case "speed" : return TRON_COMPILATION_FILTER_SPEED; 499 case "everything-profile" : return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE; 500 case "everything" : return TRON_COMPILATION_FILTER_EVERYTHING; 501 case "run-from-apk" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK; 502 case "run-from-apk-fallback" : 503 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK; 504 case "run-from-vdex-fallback" : 505 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK; 506 default: return TRON_COMPILATION_FILTER_UNKNOWN; 507 } 508 } 509 510 private static void verifyTronLoggingConstants() { 511 for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) { 512 String reason = PackageManagerServiceCompilerMapping.REASON_STRINGS[i]; 513 int value = getCompilationReasonTronValue(reason); 514 if (value == TRON_COMPILATION_REASON_ERROR 515 || value == TRON_COMPILATION_REASON_UNKNOWN) { 516 throw new IllegalArgumentException("Compilation reason not configured for TRON " 517 + "logging: " + reason); 518 } 519 } 520 } 521 522 private class ArtManagerInternalImpl extends ArtManagerInternal { 523 @Override 524 public PackageOptimizationInfo getPackageOptimizationInfo( 525 ApplicationInfo info, String abi) { 526 String compilationReason; 527 String compilationFilter; 528 try { 529 String isa = VMRuntime.getInstructionSet(abi); 530 DexFile.OptimizationInfo optInfo = 531 DexFile.getDexFileOptimizationInfo(info.getBaseCodePath(), isa); 532 compilationFilter = optInfo.getStatus(); 533 compilationReason = optInfo.getReason(); 534 } catch (FileNotFoundException e) { 535 Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e); 536 compilationFilter = "error"; 537 compilationReason = "error"; 538 } catch (IllegalArgumentException e) { 539 Slog.wtf(TAG, "Requested optimization status for " + info.getBaseCodePath() 540 + " due to an invalid abi " + abi, e); 541 compilationFilter = "error"; 542 compilationReason = "error"; 543 } 544 545 int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter); 546 int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason); 547 548 return new PackageOptimizationInfo( 549 compilationFilterTronValue, compilationReasonTronValue); 550 } 551 } 552} 553