Add SystemApis to expose runtime profile information

The API allows a system apps which acquired
{@code android.permission.READ_RUNTIME_PROFILE} to snapshot the runtime
profiles of installed packages.

The API is implemented in a new service class (AndroidRuntimeManager)
accessible from the context using
context().getPackageManager().getAndroidRuntimeManager().

The main functionality is exposed as a one way call into the
AndroidRuntimeManager with the result being posted on a callback. The
profile is available to the caller as a read-only ParcelFileDescriptor.

This CL only adds the API interfaces and validation. It does not fully
implement the functionality.

oneway void snapshotRuntimeProfile(in String packageName,
  in String codePath, in ISnapshotRuntimeProfileCallback callback)

(cherry picked from commit 45f8b29ce0)

Bug: 30934496
Test: gts-tradefed -m GtsAndroidRuntimeManagerHostTestCases

Merged-In: Iaa6be4715840f24508acba3162ea9c1ab725bd38
Change-Id: Iaa6be4715840f24508acba3162ea9c1ab725bd38
This commit is contained in:
Calin Juravle
2017-11-07 18:49:43 -08:00
committed by Andreas Gampe
parent 1b37daa810
commit 1d875ad3ae
12 changed files with 461 additions and 3 deletions

View File

@@ -133,6 +133,8 @@ java_library {
"core/java/android/content/pm/IPackageStatsObserver.aidl",
"core/java/android/content/pm/IPinItemRequest.aidl",
"core/java/android/content/pm/IShortcutService.aidl",
"core/java/android/content/pm/dex/IArtManager.aidl",
"core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
"core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
"core/java/android/database/IContentObserver.aidl",
":libcamera_client_aidl",

View File

@@ -126,6 +126,7 @@ package android {
field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
@@ -847,6 +848,7 @@ package android.content.pm {
public abstract class PackageManager {
method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
@@ -948,6 +950,24 @@ package android.content.pm {
}
package android.content.pm.dex {
public class ArtManager {
method public boolean isRuntimeProfilingEnabled();
method public void snapshotRuntimeProfile(java.lang.String, java.lang.String, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback, android.os.Handler);
field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1
field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2
field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0
}
public static abstract class ArtManager.SnapshotRuntimeProfileCallback {
ctor public ArtManager.SnapshotRuntimeProfileCallback();
method public abstract void onError(int);
method public abstract void onSuccess(android.os.ParcelFileDescriptor);
}
}
package android.content.pm.permission {
public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {

View File

@@ -56,6 +56,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
@@ -122,6 +123,8 @@ public class ApplicationPackageManager extends PackageManager {
private UserManager mUserManager;
@GuardedBy("mLock")
private PackageInstaller mInstaller;
@GuardedBy("mLock")
private ArtManager mArtManager;
@GuardedBy("mDelegates")
private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
@@ -2763,4 +2766,18 @@ public class ApplicationPackageManager extends PackageManager {
throw e.rethrowAsRuntimeException();
}
}
@Override
public ArtManager getArtManager() {
synchronized (mLock) {
if (mArtManager == null) {
try {
mArtManager = new ArtManager(mPM.getArtManager());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return mArtManager;
}
}
}

View File

@@ -48,6 +48,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
@@ -664,4 +665,6 @@ interface IPackageManager {
ComponentName getInstantAppInstallerComponent();
String getInstantAppAndroidId(String packageName, int userId);
IArtManager getArtManager();
}

View File

@@ -42,6 +42,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.ArtManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -5877,4 +5878,14 @@ public abstract class PackageManager {
@SystemApi
public abstract void registerDexModule(String dexModulePath,
@Nullable DexModuleRegisterCallback callback);
/**
* Returns the {@link ArtManager} associated with this package manager.
*
* @hide
*/
@SystemApi
public @NonNull ArtManager getArtManager() {
throw new UnsupportedOperationException("getArtManager not implemented in subclass");
}
}

View File

@@ -0,0 +1,156 @@
/**
* 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 android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Slog;
/**
* 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;
private IArtManager mArtManager;
/**
* @hide
*/
public ArtManager(@NonNull IArtManager manager) {
mArtManager = manager;
}
/**
* Snapshots the runtime profile for an apk belonging to the package {@code packageName}.
* The apk is identified by {@code codePath}. The calling process must have
* {@code android.permission.READ_RUNTIME_PROFILE} permission.
*
* The result will be posted on {@code handler} using the given {@code callback}.
* The profile being available as a read-only {@link android.os.ParcelFileDescriptor}.
*
* @param packageName the target package name
* @param codePath the code path for which the profile should be retrieved
* @param callback the callback which should be used for the result
* @param handler the handler which should be used to post the result
*/
@RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath,
@NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) {
Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
SnapshotRuntimeProfileCallbackDelegate delegate =
new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper());
try {
mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
* Returns true if runtime profiles are enabled, false otherwise.
*
* The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
*/
@RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
public boolean isRuntimeProfilingEnabled() {
try {
return mArtManager.isRuntimeProfilingEnabled();
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
return false;
}
/**
* 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
implements Handler.Callback {
private static final int MSG_SNAPSHOT_OK = 1;
private static final int MSG_ERROR = 2;
private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
private final Handler mHandler;
private SnapshotRuntimeProfileCallbackDelegate(
ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) {
mCallback = callback;
mHandler = new Handler(looper, this);
}
@Override
public void onSuccess(ParcelFileDescriptor profileReadFd) {
mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget();
}
@Override
public void onError(int errCode) {
mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget();
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_SNAPSHOT_OK:
mCallback.onSuccess((ParcelFileDescriptor) msg.obj);
break;
case MSG_ERROR:
mCallback.onError(msg.arg1);
break;
default: return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
** 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 android.content.pm.dex.ISnapshotRuntimeProfileCallback;
/**
* A system service that provides access to runtime and compiler artifacts.
*
* @hide
*/
interface IArtManager {
/**
* Snapshots the runtime profile for an apk belonging to the package {@param packageName}.
* The apk is identified by {@param codePath}. The calling process must have
* {@code android.permission.READ_RUNTIME_PROFILE} permission.
*
* The result will be posted on {@param callback} with the profile being available as a
* read-only {@link android.os.ParcelFileDescriptor}.
*/
oneway void snapshotRuntimeProfile(in String packageName,
in String codePath, in ISnapshotRuntimeProfileCallback callback);
/**
* Returns true if runtime profiles are enabled, false otherwise.
*
* The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
*/
boolean isRuntimeProfilingEnabled();
}

View File

@@ -0,0 +1,29 @@
/*
** 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 android.os.ParcelFileDescriptor;
/**
* Callback used to post the result of a profile-snapshot operation.
*
* @hide
*/
oneway interface ISnapshotRuntimeProfileCallback {
void onSuccess(in ParcelFileDescriptor profileReadFd);
void onError(int errCode);
}

View File

@@ -3609,6 +3609,11 @@
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
android:protectionLevel="signature|development|instant|appop" />
<!-- @SystemApi Allows an application to read the runtime profiles of other apps.
@hide <p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_RUNTIME_PROFILES"
android:protectionLevel="signature|privileged" />
<application android:process="system"
android:persistent="true"
android:hasCode="false"

View File

@@ -106,8 +106,6 @@ import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILUR
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -179,6 +177,7 @@ import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.IArtManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -285,6 +284,7 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DexLogger;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
@@ -292,7 +292,6 @@ import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.storage.DeviceStorageMonitorInternal;
import dalvik.system.CloseGuard;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -961,6 +960,8 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageInstallerService mInstallerService;
final ArtManagerService mArtManagerService;
private final PackageDexOptimizer mPackageDexOptimizer;
// DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
// is used by other apps).
@@ -3063,6 +3064,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
mInstallerService = new PackageInstallerService(context, this);
mArtManagerService = new ArtManagerService(this);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
@@ -24911,6 +24913,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
return mInstallerService;
}
@Override
public IArtManager getArtManager() {
return mArtManagerService;
}
private boolean userNeedsBadging(int userId) {
int index = mUserNeedsBadging.indexOfKey(userId);
if (index < 0) {

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 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 com.android.server.pm.dex;
import android.Manifest;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.dex.ArtManager;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
import android.content.pm.IPackageManager;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.os.SystemProperties;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
/**
* A system service that provides access to runtime and compiler artifacts.
*
* This service is not accessed by users directly, instead one uses an instance of
* {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
* <p/>
* {@code context().getPackageManager().getArtManager();}
* <p class="note">
* Note: Accessing runtime artifacts may require extra permissions. For example querying the
* runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
* which is a system-level permission that will not be granted to normal apps.
*/
public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final String TAG = "ArtManagerService";
private static boolean DEBUG = false;
private static boolean DEBUG_IGNORE_PERMISSIONS = false;
private final IPackageManager mPackageManager;
private final Handler mHandler;
public ArtManagerService(IPackageManager pm) {
mPackageManager = pm;
mHandler = new Handler(BackgroundThread.getHandler().getLooper());
}
@Override
public void snapshotRuntimeProfile(String packageName, String codePath,
ISnapshotRuntimeProfileCallback callback) {
// Sanity checks on the arguments.
Preconditions.checkStringNotEmpty(packageName);
Preconditions.checkStringNotEmpty(codePath);
Preconditions.checkNotNull(callback);
// Verify that the caller has the right permissions.
checkReadRuntimeProfilePermission();
if (DEBUG) {
Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
}
PackageInfo info = null;
try {
// Note that we use the default user 0 to retrieve the package info.
// This doesn't really matter because for user 0 we always get a package back (even if
// it's not installed for the user 0). It is ok because we only care about the code
// paths and not if the package is enabled or not for the user.
// TODO(calin): consider adding an API to PMS which can retrieve the
// PackageParser.Package.
info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
} catch (RemoteException ignored) {
// Should not happen.
}
if (info == null) {
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
return;
}
boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
if (!pathFound && (splitCodePaths != null)) {
for (String path : splitCodePaths) {
if (path.equals(codePath)) {
pathFound = true;
break;
}
}
}
if (!pathFound) {
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
return;
}
// All good, move forward and get the profile.
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
}
@Override
public boolean isRuntimeProfilingEnabled() {
// Verify that the caller has the right permissions.
checkReadRuntimeProfilePermission();
return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
}
/**
* Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
* on the internal {@code mHandler}.
*/
private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
int errCode) {
mHandler.post(() -> {
try {
callback.onError(errCode);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
}
});
}
/**
* Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
* If not, it throws a {@link SecurityException}.
*/
private void checkReadRuntimeProfilePermission() {
if (DEBUG_IGNORE_PERMISSIONS) {
return;
}
try {
int result = mPackageManager.checkUidPermission(
Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
if (result != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("You need "
+ Manifest.permission.READ_RUNTIME_PROFILES
+ " permission to snapshot profiles.");
}
} catch (RemoteException e) {
// Should not happen.
}
}
}

View File

@@ -47,6 +47,7 @@ import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -1184,4 +1185,12 @@ public class MockPackageManager extends PackageManager {
@Nullable DexModuleRegisterCallback callback) {
throw new UnsupportedOperationException();
}
/**
* @hide
*/
@Override
public ArtManager getArtManager() {
throw new UnsupportedOperationException();
}
}