Refactor PermissionManager.SPLIT_PERMISSIONS

Creating a SystemConfig from a non-system process is taking 500+ ms.
This CL instead exposes the needed split permissions from system_server
to optimize performance.

Tested locally and creating PermissionManager / retrieving SystemConfig
is now less than 1 ms.

Bug: 139828734
Bug: 139485700
Fixes: 139828734
Test: Added systrace / logs to PermissionController app and traced
runtime of onGrantDefaultRoles().

Change-Id: I111403e8dae3bc2b0acafc32e61aa5cd890fea29
This commit is contained in:
Anthony Hugh
2019-08-22 15:35:48 -07:00
parent d5c504d587
commit de787d4a83
7 changed files with 321 additions and 37 deletions

View File

@@ -57,6 +57,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageParserCacheHelper.ReadHelper;
import android.content.pm.PackageParserCacheHelper.WriteHelper;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
@@ -79,7 +80,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.PermissionManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructStat;
@@ -2528,11 +2528,17 @@ public class PackageParser {
Slog.i(TAG, newPermsMsg.toString());
}
List<SplitPermissionInfoParcelable> splitPermissions;
final int NS = PermissionManager.SPLIT_PERMISSIONS.size();
for (int is=0; is<NS; is++) {
final PermissionManager.SplitPermissionInfo spi =
PermissionManager.SPLIT_PERMISSIONS.get(is);
try {
splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
final int listSize = splitPermissions.size();
for (int is = 0; is < listSize; is++) {
final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk()
|| !pkg.requestedPermissions.contains(spi.getSplitPermission())) {
continue;
@@ -8501,5 +8507,4 @@ public class PackageParser {
this.error = error;
}
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2019, 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.permission;
parcelable SplitPermissionInfoParcelable;

View File

@@ -0,0 +1,202 @@
/*
* Copyright (C) 2019 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.permission;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
import com.android.internal.util.Preconditions;
import java.util.List;
import java.util.Objects;
/**
* Parcelable version of {@link android.permission.PermissionManager.SplitPermissionInfo}
* @hide
*/
@DataClass(genEqualsHashCode = true)
public class SplitPermissionInfoParcelable implements Parcelable {
/**
* The permission that is split.
*/
@NonNull
private final String mSplitPermission;
/**
* The permissions that are added.
*/
@NonNull
private final List<String> mNewPermissions;
/**
* The target API level when the permission was split.
*/
@IntRange(from = 0)
private final int mTargetSdk;
private void onConstructed() {
Preconditions.checkCollectionElementsNotNull(mNewPermissions, "newPermissions");
}
// Code below generated by codegen v1.0.0.
//
// DO NOT MODIFY!
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
//
// CHECKSTYLE:OFF Generated code
/**
* Creates a new SplitPermissionInfoParcelable.
*
* @param splitPermission
* The permission that is split.
* @param newPermissions
* The permissions that are added.
* @param targetSdk
* The target API level when the permission was split.
*/
@DataClass.Generated.Member
public SplitPermissionInfoParcelable(
@NonNull String splitPermission,
@NonNull List<String> newPermissions,
@IntRange(from = 0) int targetSdk) {
this.mSplitPermission = splitPermission;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mSplitPermission);
this.mNewPermissions = newPermissions;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mNewPermissions);
this.mTargetSdk = targetSdk;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mTargetSdk,
"from", 0);
onConstructed();
}
/**
* The permission that is split.
*/
@DataClass.Generated.Member
public @NonNull String getSplitPermission() {
return mSplitPermission;
}
/**
* The permissions that are added.
*/
@DataClass.Generated.Member
public @NonNull List<String> getNewPermissions() {
return mNewPermissions;
}
/**
* The target API level when the permission was split.
*/
@DataClass.Generated.Member
public @IntRange(from = 0) int getTargetSdk() {
return mTargetSdk;
}
@Override
@DataClass.Generated.Member
public boolean equals(Object o) {
// You can override field equality logic by defining either of the methods like:
// boolean fieldNameEquals(SplitPermissionInfoParcelable other) { ... }
// boolean fieldNameEquals(FieldType otherValue) { ... }
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
@SuppressWarnings("unchecked")
SplitPermissionInfoParcelable that = (SplitPermissionInfoParcelable) o;
//noinspection PointlessBooleanExpression
return true
&& Objects.equals(mSplitPermission, that.mSplitPermission)
&& Objects.equals(mNewPermissions, that.mNewPermissions)
&& mTargetSdk == that.mTargetSdk;
}
@Override
@DataClass.Generated.Member
public int hashCode() {
// You can override field hashCode logic by defining methods like:
// int fieldNameHashCode() { ... }
int _hash = 1;
_hash = 31 * _hash + Objects.hashCode(mSplitPermission);
_hash = 31 * _hash + Objects.hashCode(mNewPermissions);
_hash = 31 * _hash + mTargetSdk;
return _hash;
}
@Override
@DataClass.Generated.Member
public void writeToParcel(Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeString(mSplitPermission);
dest.writeStringList(mNewPermissions);
dest.writeInt(mTargetSdk);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<SplitPermissionInfoParcelable> CREATOR
= new Parcelable.Creator<SplitPermissionInfoParcelable>() {
@Override
public SplitPermissionInfoParcelable[] newArray(int size) {
return new SplitPermissionInfoParcelable[size];
}
@Override
@SuppressWarnings({"unchecked", "RedundantCast"})
public SplitPermissionInfoParcelable createFromParcel(Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
String splitPermission = in.readString();
List<String> newPermissions = new java.util.ArrayList<>();
in.readStringList(newPermissions);
int targetSdk = in.readInt();
return new SplitPermissionInfoParcelable(
splitPermission,
newPermissions,
targetSdk);
}
};
@DataClass.Generated(
time = 1567634390477L,
codegenVersion = "1.0.0",
sourceFile = "frameworks/base/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java",
inputSignatures = "private final @android.annotation.NonNull java.lang.String mSplitPermission\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mNewPermissions\nprivate final @android.annotation.IntRange(from=0L) int mTargetSdk\nprivate void onConstructed()\nclass SplitPermissionInfoParcelable extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
}

View File

@@ -19,6 +19,7 @@ package android.permission;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.permission.IOnPermissionsChangeListener;
/**
@@ -97,4 +98,6 @@ interface IPermissionManager {
String packageName, int userId);
boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId);
List<SplitPermissionInfoParcelable> getSplitPermissions();
}

View File

@@ -24,16 +24,18 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.Immutable;
import com.android.server.SystemConfig;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* System level service for accessing the permission capabilities of the platform.
@@ -44,6 +46,8 @@ import java.util.Objects;
@SystemApi
@SystemService(Context.PERMISSION_SERVICE)
public final class PermissionManager {
private static final String TAG = PermissionManager.class.getName();
/** @hide */
public static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
"permissions revoked";
@@ -51,19 +55,12 @@ public final class PermissionManager {
public static final String KILL_APP_REASON_GIDS_CHANGED =
"permission grant or revoke changed gids";
/**
* {@link android.content.pm.PackageParser} needs access without having a {@link Context}.
*
* @hide
*/
public static final ArrayList<SplitPermissionInfo> SPLIT_PERMISSIONS =
SystemConfig.getInstance().getSplitPermissions();
private final @NonNull Context mContext;
private final IPackageManager mPackageManager;
private List<SplitPermissionInfo> mSplitPermissionInfos;
/**
* Creates a new instance.
*
@@ -131,7 +128,48 @@ public final class PermissionManager {
* @return All permissions that are split.
*/
public @NonNull List<SplitPermissionInfo> getSplitPermissions() {
return SPLIT_PERMISSIONS;
if (mSplitPermissionInfos != null) {
return mSplitPermissionInfos;
}
List<SplitPermissionInfoParcelable> parcelableList;
try {
parcelableList = ActivityThread.getPermissionManager().getSplitPermissions();
} catch (RemoteException e) {
Slog.e(TAG, "Error getting split permissions", e);
return Collections.emptyList();
}
mSplitPermissionInfos = splitPermissionInfoListToNonParcelableList(parcelableList);
return mSplitPermissionInfos;
}
private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
List<SplitPermissionInfoParcelable> parcelableList) {
final int size = parcelableList.size();
List<SplitPermissionInfo> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(new SplitPermissionInfo(parcelableList.get(i)));
}
return list;
}
/**
* Converts a {@link List} of {@link SplitPermissionInfo} into a List of
* {@link SplitPermissionInfoParcelable} and returns it.
* @hide
*/
public static List<SplitPermissionInfoParcelable> splitPermissionInfoListToParcelableList(
List<SplitPermissionInfo> splitPermissionsList) {
final int size = splitPermissionsList.size();
List<SplitPermissionInfoParcelable> outList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
SplitPermissionInfo info = splitPermissionsList.get(i);
outList.add(new SplitPermissionInfoParcelable(
info.getSplitPermission(), info.getNewPermissions(), info.getTargetSdk()));
}
return outList;
}
/**
@@ -140,44 +178,40 @@ public final class PermissionManager {
*/
@Immutable
public static final class SplitPermissionInfo {
private final @NonNull String mSplitPerm;
private final @NonNull List<String> mNewPerms;
private final int mTargetSdk;
private @NonNull final SplitPermissionInfoParcelable mSplitPermissionInfoParcelable;
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SplitPermissionInfo that = (SplitPermissionInfo) o;
return mTargetSdk == that.mTargetSdk
&& mSplitPerm.equals(that.mSplitPerm)
&& mNewPerms.equals(that.mNewPerms);
return mSplitPermissionInfoParcelable.equals(that.mSplitPermissionInfoParcelable);
}
@Override
public int hashCode() {
return Objects.hash(mSplitPerm, mNewPerms, mTargetSdk);
return mSplitPermissionInfoParcelable.hashCode();
}
/**
* Get the permission that is split.
*/
public @NonNull String getSplitPermission() {
return mSplitPerm;
return mSplitPermissionInfoParcelable.getSplitPermission();
}
/**
* Get the permissions that are added.
*/
public @NonNull List<String> getNewPermissions() {
return mNewPerms;
return mSplitPermissionInfoParcelable.getNewPermissions();
}
/**
* Get the target API level when the permission was split.
*/
public int getTargetSdk() {
return mTargetSdk;
return mSplitPermissionInfoParcelable.getTargetSdk();
}
/**
@@ -191,9 +225,11 @@ public final class PermissionManager {
*/
public SplitPermissionInfo(@NonNull String splitPerm, @NonNull List<String> newPerms,
int targetSdk) {
mSplitPerm = splitPerm;
mNewPerms = newPerms;
mTargetSdk = targetSdk;
this(new SplitPermissionInfoParcelable(splitPerm, newPerms, targetSdk));
}
private SplitPermissionInfo(@NonNull SplitPermissionInfoParcelable parcelable) {
mSplitPermissionInfoParcelable = parcelable;
}
}
}

View File

@@ -53,6 +53,8 @@ import java.util.Map;
/**
* Loads global system configuration info.
* Note: Initializing this class hits the disk and is slow. This class should generally only be
* accessed by the system_server process.
*/
public class SystemConfig {
static final String TAG = "SystemConfig";
@@ -208,6 +210,11 @@ public class SystemConfig {
private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>();
public static SystemConfig getInstance() {
if (!isSystemProcess()) {
Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "
+ "system_server.");
}
synchronized (SystemConfig.class) {
if (sInstance == null) {
sInstance = new SystemConfig();
@@ -1161,4 +1168,8 @@ public class SystemConfig {
mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
}
}
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
}

View File

@@ -67,6 +67,7 @@ import android.content.pm.PackageParser.Package;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.Binder;
import android.os.Build;
@@ -2348,10 +2349,11 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// or has updated its target SDK and AR is no longer implicit to it.
// This is a compatibility workaround for apps when AR permission was
// split in Q.
int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
final List<SplitPermissionInfoParcelable> permissionList =
getSplitPermissions();
int numSplitPerms = permissionList.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
PermissionManager.SplitPermissionInfo sp =
PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
&& origPermissions.hasInstallPermission(splitPermName)) {
@@ -2920,10 +2922,10 @@ public class PermissionManagerService extends IPermissionManager.Stub {
String pkgName = pkg.packageName;
ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
final List<SplitPermissionInfoParcelable> permissionList = getSplitPermissions();
int numSplitPerms = permissionList.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
PermissionManager.SplitPermissionInfo spi =
PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
SplitPermissionInfoParcelable spi = permissionList.get(splitPermNum);
List<String> newPerms = spi.getNewPermissions();
int numNewPerms = newPerms.size();
@@ -2991,6 +2993,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return updatedUserIds;
}
@Override
public List<SplitPermissionInfoParcelable> getSplitPermissions() {
return PermissionManager.splitPermissionInfoListToParcelableList(
SystemConfig.getInstance().getSplitPermissions());
}
private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
boolean allowed = false;
final int NP = PackageParser.NEW_PERMISSIONS.length;