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