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