Home | History | Annotate | Download | only in dex
      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      */
     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