Merge "Refactor PackageParser (11/n)"

This commit is contained in:
Jackal Guo
2021-06-07 01:00:33 +00:00
committed by Android (Google) Code Review
8 changed files with 174 additions and 33 deletions

View File

@@ -723,6 +723,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable {
@Override
public ParsingPackageImpl addImplicitPermission(String permission) {
addUsesPermission(new ParsedUsesPermission(permission, 0 /*usesPermissionFlags*/));
this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
TextUtils.safeIntern(permission));
return this;

View File

@@ -74,6 +74,7 @@ import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
@@ -904,7 +905,7 @@ public class ParsingPackageUtils {
);
}
convertNewPermissions(pkg);
convertCompatPermissions(pkg);
convertSplitPermissions(pkg);
@@ -2790,31 +2791,16 @@ public class ParsingPackageUtils {
}
}
private static void convertNewPermissions(ParsingPackage pkg) {
final int NP = PackageParser.NEW_PERMISSIONS.length;
StringBuilder newPermsMsg = null;
for (int ip = 0; ip < NP; ip++) {
final PackageParser.NewPermissionInfo npi
= PackageParser.NEW_PERMISSIONS[ip];
if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
private static void convertCompatPermissions(ParsingPackage pkg) {
for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
if (pkg.getTargetSdkVersion() >= info.sdkVersion) {
break;
}
if (!pkg.getRequestedPermissions().contains(npi.name)) {
if (newPermsMsg == null) {
newPermsMsg = new StringBuilder(128);
newPermsMsg.append(pkg.getPackageName());
newPermsMsg.append(": compat added ");
} else {
newPermsMsg.append(' ');
}
newPermsMsg.append(npi.name);
pkg.addUsesPermission(new ParsedUsesPermission(npi.name, 0))
.addImplicitPermission(npi.name);
if (!pkg.getRequestedPermissions().contains(info.name)) {
pkg.addImplicitPermission(info.name);
}
}
if (newPermsMsg != null) {
Slog.i(TAG, newPermsMsg.toString());
}
}
private void convertSplitPermissions(ParsingPackage pkg) {
@@ -2830,8 +2816,7 @@ public class ParsingPackageUtils {
for (int in = 0; in < newPerms.size(); in++) {
final String perm = newPerms.get(in);
if (!requestedPermissions.contains(perm)) {
pkg.addUsesPermission(new ParsedUsesPermission(perm, 0))
.addImplicitPermission(perm);
pkg.addImplicitPermission(perm);
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2021 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.Manifest;
import android.content.pm.parsing.component.ParsedUsesPermission;
/**
* Implements compatibility support for permissions, and old applications
* will be automatically granted it.
*
* Compatibility permissions are permissions that are automatically granted to
* packages that target an SDK prior to when the permission was introduced.
* Sometimes the platform makes breaking behaviour changes and hides the legacy
* behaviour behind a permission. In these instances, we ensure applications
* targeting older platform versions are implicitly granted the correct set of
* permissions.
*
* @hide
*/
public class CompatibilityPermissionInfo extends ParsedUsesPermission {
public final int sdkVersion;
/**
* List of new permissions that have been added since 1.0.
*
* @hide
*/
public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
new CompatibilityPermissionInfo[]{
new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/)
};
private CompatibilityPermissionInfo(String name, int sdkVersion,
@UsesPermissionFlags int usesPermissionFlags) {
super(name, usesPermissionFlags);
this.sdkVersion = sdkVersion;
}
}

View File

@@ -82,13 +82,13 @@ import android.content.pm.PackageManager.PermissionGroupInfoFlags;
import android.content.pm.PackageManager.PermissionInfoFlags;
import android.content.pm.PackageManager.PermissionWhitelistFlags;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.SigningDetails;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.permission.CompatibilityPermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
@@ -2822,7 +2822,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
if (!isNewPlatformPermissionForPackage(perm, pkg)) {
if (!isCompatPlatformPermissionForPackage(perm, pkg)) {
shouldGrantNormalPermission = false;
}
}
@@ -3358,14 +3358,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return result;
}
private static boolean isNewPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) {
boolean allowed = false;
final int NP = PackageParser.NEW_PERMISSIONS.length;
for (int ip=0; ip<NP; ip++) {
final PackageParser.NewPermissionInfo npi
= PackageParser.NEW_PERMISSIONS[ip];
if (npi.name.equals(perm)
&& pkg.getTargetSdkVersion() < npi.sdkVersion) {
for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) {
final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i];
if (info.name.equals(perm)
&& pkg.getTargetSdkVersion() < info.sdkVersion) {
allowed = true;
Log.i(TAG, "Auto-granting " + perm + " to old pkg "
+ pkg.getPackageName());

View File

@@ -117,6 +117,7 @@ android_test {
":PackageParserTestApp2",
":PackageParserTestApp3",
":PackageParserTestApp4",
":PackageParserTestApp5",
":apex.test",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",

View File

@@ -15,6 +15,10 @@
*/
package com.android.server.pm;
import static android.content.pm.permission.CompatibilityPermissionInfo.COMPAT_PERMS;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -25,6 +29,7 @@ import static org.junit.Assert.fail;
import static java.lang.Boolean.TRUE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.stream.Collectors.toList;
import android.annotation.NonNull;
import android.content.Context;
@@ -83,6 +88,7 @@ import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
@@ -106,6 +112,7 @@ public class PackageParserTest {
private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
private static final String TEST_APP5_APK = "PackageParserTestApp5.apk";
private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
@Before
@@ -506,6 +513,56 @@ public class PackageParserTest {
}
}
@Test
public void testParseModernPackageHasNoCompatPermissions() throws Exception {
final File testFile = extractFile(TEST_APP1_APK);
try {
final ParsedPackage pkg = new TestPackageParser2()
.parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
final List<String> compatPermissions =
Arrays.stream(COMPAT_PERMS).map(p -> p.name).collect(toList());
assertWithMessage(
"Compatibility permissions shouldn't be added into uses permissions.")
.that(pkg.getUsesPermissions().stream().map(p -> p.name).collect(toList()))
.containsNoneIn(compatPermissions);
assertWithMessage(
"Compatibility permissions shouldn't be added into requested permissions.")
.that(pkg.getRequestedPermissions()).containsNoneIn(compatPermissions);
assertWithMessage(
"Compatibility permissions shouldn't be added into implicit permissions.")
.that(pkg.getImplicitPermissions()).containsNoneIn(compatPermissions);
} finally {
testFile.delete();
}
}
@Test
public void testParseLegacyPackageHasCompatPermissions() throws Exception {
final File testFile = extractFile(TEST_APP5_APK);
try {
final ParsedPackage pkg = new TestPackageParser2()
.parsePackage(testFile, 0 /*flags*/, false /*useCaches*/);
assertWithMessage(
"Compatibility permissions should be added into uses permissions.")
.that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
.allMatch(pkg.getUsesPermissions().stream().map(p -> p.name)
.collect(toList())::contains))
.isTrue();
assertWithMessage(
"Compatibility permissions should be added into requested permissions.")
.that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
.allMatch(pkg.getRequestedPermissions()::contains))
.isTrue();
assertWithMessage(
"Compatibility permissions should be added into implicit permissions.")
.that(Arrays.stream(COMPAT_PERMS).map(p -> p.name)
.allMatch(pkg.getImplicitPermissions()::contains))
.isTrue();
} finally {
testFile.delete();
}
}
/**
* A trivial subclass of package parser that only caches the package name, and throws away
* all other information.

View File

@@ -74,3 +74,17 @@ android_test_helper_app {
resource_dirs: ["res"],
manifest: "AndroidManifestApp4.xml",
}
android_test_helper_app {
name: "PackageParserTestApp5",
sdk_version: "current",
srcs: ["**/*.java"],
dex_preopt: {
enabled: false,
},
optimize: {
enabled: false,
},
resource_dirs: ["res"],
manifest: "AndroidManifestApp5.xml",
}

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.servicestests.apps.packageparserapp" >
<uses-sdk android:minSdkVersion="3"
android:targetSdkVersion="3" />
<application>
<activity android:name=".TestActivity"
android:exported="true" />
</application>
</manifest>