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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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() {}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user