ArtManager.java revision 0ad6283da79c5f5e7cda0dd10030b9d4be2cb6e8
1/**
2 * Copyright 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 android.content.pm.dex;
18
19import static android.Manifest.permission.PACKAGE_USAGE_STATS;
20import static android.Manifest.permission.READ_RUNTIME_PROFILES;
21
22import android.annotation.CallbackExecutor;
23import android.annotation.IntDef;
24import android.annotation.NonNull;
25import android.annotation.Nullable;
26import android.annotation.RequiresPermission;
27import android.annotation.SystemApi;
28import android.content.Context;
29import android.os.Environment;
30import android.os.ParcelFileDescriptor;
31import android.os.RemoteException;
32import android.util.Slog;
33
34import java.io.File;
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37import java.util.concurrent.Executor;
38
39/**
40 * Class for retrieving various kinds of information related to the runtime artifacts of
41 * packages that are currently installed on the device.
42 *
43 * @hide
44 */
45@SystemApi
46public class ArtManager {
47    private static final String TAG = "ArtManager";
48
49    /** The snapshot failed because the package was not found. */
50    public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0;
51    /** The snapshot failed because the package code path does not exist. */
52    public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1;
53    /** The snapshot failed because of an internal error (e.g. error during opening profiles). */
54    public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
55
56    /** Constant used for applications profiles. */
57    public static final int PROFILE_APPS = 0;
58    /** Constant used for the boot image profile. */
59    public static final int PROFILE_BOOT_IMAGE = 1;
60
61    /** @hide */
62    @IntDef(flag = true, prefix = { "PROFILE_" }, value = {
63            PROFILE_APPS,
64            PROFILE_BOOT_IMAGE,
65    })
66    @Retention(RetentionPolicy.SOURCE)
67    public @interface ProfileType {}
68
69    private final Context mContext;
70    private final IArtManager mArtManager;
71
72    /**
73     * @hide
74     */
75    public ArtManager(@NonNull Context context, @NonNull IArtManager manager) {
76        mContext = context;
77        mArtManager = manager;
78    }
79
80    /**
81     * Snapshots a runtime profile according to the {@code profileType} parameter.
82     *
83     * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot
84     * the profile for for an apk belonging to the package {@code packageName}.
85     * The apk is identified by {@code codePath}.
86     *
87     * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot
88     * the profile for the boot image. In this case {@code codePath can be null}. The parameters
89     * {@code packageName} and {@code codePath} are ignored.
90     *u
91     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
92     *
93     * The result will be posted on the {@code executor} using the given {@code callback}.
94     * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
95     *
96     * This method will throw {@link IllegalStateException} if
97     * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
98     * {@code profileType}.
99     *
100     * @param profileType the type of profile that should be snapshot (boot image or app)
101     * @param packageName the target package name or null if the target is the boot image
102     * @param codePath the code path for which the profile should be retrieved or null if
103     *                 the target is the boot image
104     * @param callback the callback which should be used for the result
105     * @param executor the executor which should be used to post the result
106     */
107    @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
108    public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
109            @Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
110            @NonNull SnapshotRuntimeProfileCallback callback) {
111        Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
112
113        SnapshotRuntimeProfileCallbackDelegate delegate =
114                new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
115        try {
116            mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate,
117                    mContext.getOpPackageName());
118        } catch (RemoteException e) {
119            throw e.rethrowAsRuntimeException();
120        }
121    }
122
123    /**
124     * Returns true if runtime profiles are enabled for the given type, false otherwise.
125     *
126     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
127     *
128     * @param profileType can be either {@link ArtManager#PROFILE_APPS}
129     *                    or {@link ArtManager#PROFILE_BOOT_IMAGE}
130     */
131    @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS })
132    public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
133        try {
134            return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName());
135        } catch (RemoteException e) {
136            throw e.rethrowAsRuntimeException();
137        }
138    }
139
140    /**
141     * Callback used for retrieving runtime profiles.
142     */
143    public abstract static class SnapshotRuntimeProfileCallback {
144        /**
145         * Called when the profile snapshot finished with success.
146         *
147         * @param profileReadFd the file descriptor that can be used to read the profile. Note that
148         *                      the file might be empty (which is valid profile).
149         */
150        public abstract void onSuccess(ParcelFileDescriptor profileReadFd);
151
152        /**
153         * Called when the profile snapshot finished with an error.
154         *
155         * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND,
156         *      SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}.
157         */
158        public abstract void onError(int errCode);
159    }
160
161    private static class SnapshotRuntimeProfileCallbackDelegate
162            extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub {
163        private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
164        private final Executor mExecutor;
165
166        private SnapshotRuntimeProfileCallbackDelegate(
167                ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) {
168            mCallback = callback;
169            mExecutor = executor;
170        }
171
172        @Override
173        public void onSuccess(final ParcelFileDescriptor profileReadFd) {
174            mExecutor.execute(() -> mCallback.onSuccess(profileReadFd));
175        }
176
177        @Override
178        public void onError(int errCode) {
179            mExecutor.execute(() -> mCallback.onError(errCode));
180        }
181    }
182
183    /**
184     * Return the profile name for the given split. If {@code splitName} is null the
185     * method returns the profile name for the base apk.
186     *
187     * @hide
188     */
189    public static String getProfileName(String splitName) {
190        return splitName == null ? "primary.prof" : splitName + ".split.prof";
191    }
192
193    /**
194     * Return the path to the current profile corresponding to given package and split.
195     *
196     * @hide
197     */
198    public static String getCurrentProfilePath(String packageName, int userId, String splitName) {
199        File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName);
200        return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
201    }
202
203    /**
204     * Return the snapshot profile file for the given package and profile name.
205     *
206     * KEEP in sync with installd dexopt.cpp.
207     * TODO(calin): inject the snapshot profile name from PM to avoid the dependency.
208     *
209     * @hide
210     */
211    public static File getProfileSnapshotFileForName(String packageName, String profileName) {
212        File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
213        return new File(profileDir, profileName  + ".snapshot");
214    }
215}
216