Merge "Define a new manifest attribute targetDisplayCategory in <activity> tag and parse into ActivityInfo through string validation"

This commit is contained in:
Mary Xia
2022-10-24 17:43:52 +00:00
committed by Android (Google) Code Review
14 changed files with 171 additions and 5 deletions

View File

@@ -1492,6 +1492,7 @@ package android {
field public static final int targetCellWidth = 16844340; // 0x1010634
field public static final int targetClass = 16842799; // 0x101002f
field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
field public static final int targetDisplayCategory;
field public static final int targetId = 16843740; // 0x10103dc
field public static final int targetName = 16843853; // 0x101044d
field public static final int targetPackage = 16842785; // 0x1010021
@@ -11138,6 +11139,7 @@ package android.content.pm {
field public int screenOrientation;
field public int softInputMode;
field public String targetActivity;
field @Nullable public String targetDisplayCategory;
field public String taskAffinity;
field public int theme;
field public int uiOptions;

View File

@@ -220,6 +220,23 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
*/
public String launchToken;
/**
* Specifies the category of the target display the activity is expected to run on. Set from
* the {@link android.R.attr#targetDisplayCategory} attribute. Upon creation, a virtual display
* can specify which display categories it supports and one of the category must be present in
* the activity's manifest to allow this activity to run. The default value is {@code null},
* which indicates the activity does not belong to a restricted display category and thus can
* only run on a display that didn't specify any display categories. Each activity can only
* specify one category it targets to but a virtual display can support multiple restricted
* categories.
*
* This field should be formatted as a Java-language-style free form string(for example,
* com.google.automotive_entertainment), which may contain uppercase or lowercase letters ('A'
* through 'Z'), numbers, and underscores ('_') but may only start with letters.
*/
@Nullable
public String targetDisplayCategory;
/**
* Activity can not be resized and always occupies the fullscreen area with all windows fully
* visible.
@@ -1313,6 +1330,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
mMaxAspectRatio = orig.mMaxAspectRatio;
mMinAspectRatio = orig.mMinAspectRatio;
supportsSizeChanges = orig.supportsSizeChanges;
targetDisplayCategory = orig.targetDisplayCategory;
}
/**
@@ -1651,6 +1669,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
if (mKnownActivityEmbeddingCerts != null) {
pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
}
if (targetDisplayCategory != null) {
pw.println(prefix + "targetDisplayCategory=" + targetDisplayCategory);
}
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1697,6 +1718,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
dest.writeString8(targetDisplayCategory);
}
/**
@@ -1822,6 +1844,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
if (mKnownActivityEmbeddingCerts.isEmpty()) {
mKnownActivityEmbeddingCerts = null;
}
targetDisplayCategory = source.readString8();
}
/**

View File

@@ -3062,6 +3062,20 @@
<attr name="canDisplayOnRemoteDevices" format="boolean"/>
<attr name="allowUntrustedActivityEmbedding" />
<attr name="knownActivityEmbeddingCerts" />
<!-- Specifies the category of the target display the activity is expected to run on. Upon
creation, a virtual display can specify which display categories it supports and one of
the category must be present in the activity's manifest to allow this activity to run.
The default value is {@code null}, which indicates the activity does not belong to a
restricted display category and thus can only run on a display that didn't specify any
display categories. Each activity can only specify one category it targets to but a
virtual display can accommodate multiple restricted categories.
<p> This field should be formatted as a Java-language-style free form string(for
example, com.google.automotive_entertainment), which may contain uppercase or lowercase
letters ('A' through 'Z'), numbers, and underscores ('_') but may only start with
letters.
-->
<attr name="targetDisplayCategory" format="string"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new

View File

@@ -116,6 +116,7 @@
<public name="handwritingBoundsOffsetBottom" />
<public name="accessibilityDataPrivate" />
<public name="enableTextStylingShortcuts" />
<public name="targetDisplayCategory"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01cd0000">

View File

@@ -570,6 +570,7 @@ public class PackageInfoUtils {
ai.metaData = null;
}
ai.applicationInfo = applicationInfo;
ai.targetDisplayCategory = a.getTargetDisplayCategory();
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
return ai;

View File

@@ -96,4 +96,10 @@ public interface ParsedActivity extends ParsedMainComponent {
ActivityInfo.WindowLayout getWindowLayout();
boolean isSupportsSizeChanges();
/**
* Gets the category of the target display this activity is supposed to run on.
*/
@Nullable
String getTargetDisplayCategory();
}

View File

@@ -96,6 +96,9 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
@Nullable
private ActivityInfo.WindowLayout windowLayout;
@Nullable
private String mTargetDisplayCategory;
public ParsedActivityImpl(ParsedActivityImpl other) {
super(other);
this.theme = other.theme;
@@ -122,6 +125,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
this.colorMode = other.colorMode;
this.windowLayout = other.windowLayout;
this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
this.mTargetDisplayCategory = other.mTargetDisplayCategory;
}
/**
@@ -189,6 +193,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
alias.requestedVrComponent = target.getRequestedVrComponent();
alias.setDirectBootAware(target.isDirectBootAware());
alias.setProcessName(target.getProcessName());
alias.setTargetDisplayCategory(target.getTargetDisplayCategory());
return alias;
// Not all attributes from the target ParsedActivity are copied to the alias.
@@ -316,6 +321,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
dest.writeBoolean(false);
}
sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
dest.writeString8(this.mTargetDisplayCategory);
}
public ParsedActivityImpl() {
@@ -350,6 +356,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
windowLayout = new ActivityInfo.WindowLayout(in);
}
this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
this.mTargetDisplayCategory = in.readString8();
}
@NonNull
@@ -406,7 +413,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
@Nullable String requestedVrComponent,
int rotationAnimation,
int colorMode,
@Nullable ActivityInfo.WindowLayout windowLayout) {
@Nullable ActivityInfo.WindowLayout windowLayout,
@Nullable String targetDisplayCategory) {
this.theme = theme;
this.uiOptions = uiOptions;
this.targetActivity = targetActivity;
@@ -431,6 +439,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
this.rotationAnimation = rotationAnimation;
this.colorMode = colorMode;
this.windowLayout = windowLayout;
this.mTargetDisplayCategory = targetDisplayCategory;
// onConstructed(); // You can define this method to get a callback
}
@@ -550,6 +559,11 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
return windowLayout;
}
@DataClass.Generated.Member
public @Nullable String getTargetDisplayCategory() {
return mTargetDisplayCategory;
}
@DataClass.Generated.Member
public @NonNull ParsedActivityImpl setTheme( int value) {
theme = value;
@@ -676,11 +690,17 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
return this;
}
@DataClass.Generated.Member
public @NonNull ParsedActivityImpl setTargetDisplayCategory(@NonNull String value) {
mTargetDisplayCategory = value;
return this;
}
@DataClass.Generated(
time = 1644372875433L,
time = 1664805688714L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mTargetDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
@Deprecated
private void __metadata() {}

View File

@@ -29,6 +29,7 @@ import android.app.ActivityTaskManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseInput.DeferredError;
import android.content.pm.parsing.result.ParseResult;
@@ -219,6 +220,18 @@ public class ParsedActivityUtils {
pkg.setVisibleToInstantApps(true);
}
String targetDisplayCategory = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_targetDisplayCategory, 0);
if (targetDisplayCategory != null
&& FrameworkParsingPackageUtils.validateName(targetDisplayCategory,
false /* requireSeparator */, false /* requireFilename */) != null) {
return input.error("targetDisplayCategory attribute can only consists of "
+ "alphanumeric characters, '_', and '.'");
}
activity.setTargetDisplayCategory(targetDisplayCategory);
return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
false /*isAlias*/, visibleToEphemeral, input,
R.styleable.AndroidManifestActivity_parentActivityName,

View File

@@ -53,7 +53,8 @@ class ParsedActivityTest : ParsedMainComponentTest(
ParsedActivity::getTaskAffinity,
ParsedActivity::getTheme,
ParsedActivity::getUiOptions,
ParsedActivity::isSupportsSizeChanges
ParsedActivity::isSupportsSizeChanges,
ParsedActivity::getTargetDisplayCategory
)
override fun mainComponentSubclassExtraParams() = listOf(

View File

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

View File

@@ -23,6 +23,7 @@ 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;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -127,6 +128,7 @@ public class PackageParserTest {
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 TEST_APP6_APK = "PackageParserTestApp6.apk";
private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
@Before
@@ -331,6 +333,46 @@ public class PackageParserTest {
}
}
@Test
public void testParseActivityTargetDisplayCategoryValid() throws Exception {
final File testFile = extractFile(TEST_APP4_APK);
String actualDisplayCategory = null;
try {
final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
final List<ParsedActivity> activities = pkg.getActivities();
for (ParsedActivity activity : activities) {
if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
actualDisplayCategory = activity.getTargetDisplayCategory();
}
}
} finally {
testFile.delete();
}
assertEquals("automotive", actualDisplayCategory);
}
@Test
public void testParseActivityTargetDisplayCategoryInvalid() throws Exception {
final File testFile = extractFile(TEST_APP6_APK);
String actualDisplayCategory = null;
try {
final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
final List<ParsedActivity> activities = pkg.getActivities();
for (ParsedActivity activity : activities) {
if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
actualDisplayCategory = activity.getTargetDisplayCategory();
}
}
} catch (PackageManagerException e) {
assertThat(e.getMessage()).contains(
"targetDisplayCategory attribute can only consists"
+ " of alphanumeric characters, '_', and '.'");
} finally {
testFile.delete();
}
assertNotEquals("$automotive", actualDisplayCategory);
}
private static final int PROPERTY_TYPE_BOOLEAN = 1;
private static final int PROPERTY_TYPE_FLOAT = 2;
private static final int PROPERTY_TYPE_INTEGER = 3;

View File

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

View File

@@ -31,7 +31,8 @@
<property android:name="android.cts.PROPERTY_STRING_VIA_RESOURCE" android:value="@string/string_property" />
<activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
android:exported="true" >
android:exported="true"
android:targetDisplayCategory="automotive">
<property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" />

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 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" >
<application>
<activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
android:exported="true"
android:targetDisplayCategory="$automotive">
</activity>
</application>
</manifest>