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