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 
17 package android.content.pm.dex;
18 
19 import static android.Manifest.permission.PACKAGE_USAGE_STATS;
20 import static android.Manifest.permission.READ_RUNTIME_PROFILES;
21 
22 import android.annotation.CallbackExecutor;
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.RequiresPermission;
27 import android.annotation.SystemApi;
28 import android.content.Context;
29 import android.os.Environment;
30 import android.os.ParcelFileDescriptor;
31 import android.os.RemoteException;
32 import android.util.Slog;
33 
34 import java.io.File;
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import 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
46 public 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      */
ArtManager(@onNull Context context, @NonNull IArtManager manager)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 })
snapshotRuntimeProfile(@rofileType int profileType, @Nullable String packageName, @Nullable String codePath, @NonNull @CallbackExecutor Executor executor, @NonNull SnapshotRuntimeProfileCallback callback)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 })
isRuntimeProfilingEnabled(@rofileType int profileType)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          */
onSuccess(ParcelFileDescriptor profileReadFd)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          */
onError(int errCode)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 
SnapshotRuntimeProfileCallbackDelegate( ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor)166         private SnapshotRuntimeProfileCallbackDelegate(
167                 ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) {
168             mCallback = callback;
169             mExecutor = executor;
170         }
171 
172         @Override
onSuccess(final ParcelFileDescriptor profileReadFd)173         public void onSuccess(final ParcelFileDescriptor profileReadFd) {
174             mExecutor.execute(() -> mCallback.onSuccess(profileReadFd));
175         }
176 
177         @Override
onError(int errCode)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      */
getProfileName(String splitName)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      */
getCurrentProfilePath(String packageName, int userId, String splitName)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      */
getProfileSnapshotFileForName(String packageName, String profileName)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