Merge changes from topic "cross_uid_embedding_verification"
* changes: Verify cross-uid activity embedding Add Cross-UID ActivityEmbedding trust model APIs
This commit is contained in:
committed by
Android (Google) Code Review
commit
8eb30b3b30
@@ -343,6 +343,7 @@ package android {
|
||||
field public static final int allowSingleTap = 16843353; // 0x1010259
|
||||
field public static final int allowTaskReparenting = 16843268; // 0x1010204
|
||||
field public static final int allowUndo = 16843999; // 0x10104df
|
||||
field public static final int allowUntrustedActivityEmbedding;
|
||||
field public static final int alpha = 16843551; // 0x101031f
|
||||
field public static final int alphabeticModifiers = 16844110; // 0x101054e
|
||||
field public static final int alphabeticShortcut = 16843235; // 0x10101e3
|
||||
@@ -907,6 +908,7 @@ package android {
|
||||
field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
|
||||
field public static final int keycode = 16842949; // 0x10100c5
|
||||
field public static final int killAfterRestore = 16843420; // 0x101029c
|
||||
field public static final int knownActivityEmbeddingCerts;
|
||||
field public static final int knownCerts = 16844330; // 0x101062a
|
||||
field public static final int lStar = 16844359; // 0x1010647
|
||||
field public static final int label = 16842753; // 0x1010001
|
||||
@@ -10969,6 +10971,7 @@ package android.content.pm {
|
||||
ctor public ActivityInfo(android.content.pm.ActivityInfo);
|
||||
method public int describeContents();
|
||||
method public void dump(android.util.Printer, String);
|
||||
method @NonNull public java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts();
|
||||
method public final int getThemeResource();
|
||||
field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
|
||||
field public static final int COLOR_MODE_HDR = 2; // 0x2
|
||||
@@ -10996,6 +10999,7 @@ package android.content.pm {
|
||||
field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
|
||||
field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
|
||||
field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
|
||||
field public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 268435456; // 0x10000000
|
||||
field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
|
||||
field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
|
||||
field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4
|
||||
@@ -11085,6 +11089,7 @@ package android.content.pm {
|
||||
method public void dump(android.util.Printer, String);
|
||||
method public static CharSequence getCategoryTitle(android.content.Context, int);
|
||||
method public int getGwpAsanMode();
|
||||
method @NonNull public java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts();
|
||||
method public int getMemtagMode();
|
||||
method public int getNativeHeapZeroInitialized();
|
||||
method public int getRequestRawExternalStorageAccess();
|
||||
|
||||
@@ -18,6 +18,8 @@ package android.content.pm;
|
||||
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.Activity;
|
||||
import android.app.compat.CompatChanges;
|
||||
@@ -35,10 +37,16 @@ import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Printer;
|
||||
|
||||
import com.android.internal.util.Parcelling;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Information you can retrieve about a particular application
|
||||
@@ -48,6 +56,9 @@ import java.lang.annotation.RetentionPolicy;
|
||||
*/
|
||||
public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
|
||||
private static final Parcelling.BuiltIn.ForStringSet sForStringSet =
|
||||
Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
|
||||
|
||||
// NOTE: When adding new data members be sure to update the copy-constructor, Parcel
|
||||
// constructor, and writeToParcel.
|
||||
|
||||
@@ -524,6 +535,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
*/
|
||||
public static final int FLAG_PREFER_MINIMAL_POST_PROCESSING = 0x2000000;
|
||||
|
||||
/**
|
||||
* Bit in {@link #flags}: If set, indicates that the activity can be embedded by untrusted
|
||||
* hosts. In this case the interactions with and visibility of the embedded activity may be
|
||||
* limited. Set from the {@link android.R.attr#allowUntrustedActivityEmbedding} attribute.
|
||||
*/
|
||||
public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 0x10000000;
|
||||
|
||||
/**
|
||||
* @hide Bit in {@link #flags}: If set, this component will only be seen
|
||||
* by the system user. Only works with broadcast receivers. Set from the
|
||||
@@ -561,7 +579,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
* {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS},
|
||||
* {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY},
|
||||
* {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS},
|
||||
* {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}.
|
||||
* {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER},
|
||||
* {@link #FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING}.
|
||||
*/
|
||||
public int flags;
|
||||
|
||||
@@ -1079,6 +1098,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
|
||||
private static final long CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW = 197654537L;
|
||||
|
||||
/**
|
||||
* Optional set of a certificates identifying apps that are allowed to embed this activity. From
|
||||
* the "knownActivityEmbeddingCerts" attribute.
|
||||
*/
|
||||
@Nullable
|
||||
private Set<String> mKnownActivityEmbeddingCerts;
|
||||
|
||||
/**
|
||||
* Convert Java change bits to native.
|
||||
*
|
||||
@@ -1227,6 +1253,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
launchMode = orig.launchMode;
|
||||
documentLaunchMode = orig.documentLaunchMode;
|
||||
permission = orig.permission;
|
||||
mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts;
|
||||
taskAffinity = orig.taskAffinity;
|
||||
targetActivity = orig.targetActivity;
|
||||
flags = orig.flags;
|
||||
@@ -1442,6 +1469,31 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
return mMinAspectRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the trusted host certificate digests of apps that are allowed to embed this activity.
|
||||
* The digests are computed using the SHA-256 digest algorithm.
|
||||
* @see android.R.attr#knownActivityEmbeddingCerts
|
||||
*/
|
||||
@NonNull
|
||||
public Set<String> getKnownActivityEmbeddingCerts() {
|
||||
return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
|
||||
: mKnownActivityEmbeddingCerts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trusted host certificates of apps that are allowed to embed this activity.
|
||||
* @see #getKnownActivityEmbeddingCerts()
|
||||
* @hide
|
||||
*/
|
||||
public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
|
||||
// Convert the provided digest to upper case for consistent Set membership
|
||||
// checks when verifying the signing certificate digests of requesting apps.
|
||||
mKnownActivityEmbeddingCerts = new ArraySet<>();
|
||||
for (String knownCert : knownActivityEmbeddingCerts) {
|
||||
mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isChangeEnabled(long changeId) {
|
||||
return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
|
||||
UserHandle.getUserHandleForUid(applicationInfo.uid));
|
||||
@@ -1573,6 +1625,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
if (supportsSizeChanges) {
|
||||
pw.println(prefix + "supportsSizeChanges=true");
|
||||
}
|
||||
if (mKnownActivityEmbeddingCerts != null) {
|
||||
pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
|
||||
}
|
||||
super.dumpBack(pw, prefix, dumpFlags);
|
||||
}
|
||||
|
||||
@@ -1618,6 +1673,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
dest.writeFloat(mMaxAspectRatio);
|
||||
dest.writeFloat(mMinAspectRatio);
|
||||
dest.writeBoolean(supportsSizeChanges);
|
||||
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1739,6 +1795,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
|
||||
mMaxAspectRatio = source.readFloat();
|
||||
mMinAspectRatio = source.readFloat();
|
||||
supportsSizeChanges = source.readBoolean();
|
||||
mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
|
||||
if (mKnownActivityEmbeddingCerts.isEmpty()) {
|
||||
mKnownActivityEmbeddingCerts = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.os.Parcelable;
|
||||
import android.os.UserHandle;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Printer;
|
||||
import android.util.SparseArray;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
@@ -52,7 +53,9 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@@ -62,6 +65,8 @@ import java.util.UUID;
|
||||
*/
|
||||
public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
|
||||
private static final Parcelling.BuiltIn.ForStringSet sForStringSet =
|
||||
Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
|
||||
|
||||
/**
|
||||
* Default task affinity of all activities in this application. See
|
||||
@@ -1550,6 +1555,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
*/
|
||||
private int localeConfigRes;
|
||||
|
||||
/**
|
||||
* Optional set of a certificates identifying apps that are allowed to embed activities of this
|
||||
* application. From the "knownActivityEmbeddingCerts" attribute.
|
||||
*/
|
||||
@Nullable
|
||||
private Set<String> mKnownActivityEmbeddingCerts;
|
||||
|
||||
public void dump(Printer pw, String prefix) {
|
||||
dump(pw, prefix, DUMP_FLAG_ALL);
|
||||
}
|
||||
@@ -1673,6 +1685,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
}
|
||||
}
|
||||
pw.println(prefix + "createTimestamp=" + createTimestamp);
|
||||
if (mKnownActivityEmbeddingCerts != null) {
|
||||
pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
|
||||
}
|
||||
super.dumpBack(pw, prefix);
|
||||
}
|
||||
|
||||
@@ -1787,6 +1802,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
}
|
||||
proto.end(detailToken);
|
||||
}
|
||||
if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) {
|
||||
for (String knownCert : mKnownActivityEmbeddingCerts) {
|
||||
proto.write(ApplicationInfoProto.KNOWN_ACTIVITY_EMBEDDING_CERTS, knownCert);
|
||||
}
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
@@ -1837,6 +1857,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
super(orig);
|
||||
taskAffinity = orig.taskAffinity;
|
||||
permission = orig.permission;
|
||||
mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts;
|
||||
processName = orig.processName;
|
||||
className = orig.className;
|
||||
theme = orig.theme;
|
||||
@@ -2006,6 +2027,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
}
|
||||
}
|
||||
dest.writeInt(localeConfigRes);
|
||||
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
|
||||
@@ -2102,6 +2124,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
}
|
||||
}
|
||||
localeConfigRes = source.readInt();
|
||||
mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
|
||||
if (mKnownActivityEmbeddingCerts.isEmpty()) {
|
||||
mKnownActivityEmbeddingCerts = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2658,7 +2684,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
return localeConfigRes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List of all shared libraries this application is linked against. This
|
||||
* list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
|
||||
@@ -2675,4 +2700,29 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
||||
return sharedLibraryInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the trusted host certificate digests of apps that are allowed to embed activities of
|
||||
* this application. The digests are computed using the SHA-256 digest algorithm.
|
||||
* @see android.R.attr#knownActivityEmbeddingCerts
|
||||
*/
|
||||
@NonNull
|
||||
public Set<String> getKnownActivityEmbeddingCerts() {
|
||||
return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
|
||||
: mKnownActivityEmbeddingCerts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trusted host certificates of apps that are allowed to embed activities of this
|
||||
* application.
|
||||
* @see #getKnownActivityEmbeddingCerts()
|
||||
* @hide
|
||||
*/
|
||||
public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
|
||||
// Convert the provided digest to upper case for consistent Set membership
|
||||
// checks when verifying the signing certificate digests of requesting apps.
|
||||
mKnownActivityEmbeddingCerts = new ArraySet<>();
|
||||
for (String knownCert : knownActivityEmbeddingCerts) {
|
||||
mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,4 +115,5 @@ message ApplicationInfoProto {
|
||||
}
|
||||
optional Detail detail = 17;
|
||||
repeated string overlay_paths = 18;
|
||||
repeated string known_activity_embedding_certs = 19;
|
||||
}
|
||||
|
||||
@@ -1048,6 +1048,24 @@
|
||||
<p>The default value of this attribute is <code>false</code>. -->
|
||||
<attr name="allowEmbedded" format="boolean" />
|
||||
|
||||
<!-- A reference to an array resource containing the signing certificate digests, one of which a
|
||||
client is required to be signed with in order to embed the activity. If the client is not
|
||||
signed with one of the certificates in the set, and the activity does not allow embedding
|
||||
by untrusted hosts via {@link android.R.attr#allowUntrustedActivityEmbedding} flag, the
|
||||
embedding request will fail.
|
||||
<p>The digest should be computed over the DER encoding of the trusted certificate using the
|
||||
SHA-256 digest algorithm.
|
||||
<p>If only a single signer is declared this can also be a string resource, or the digest
|
||||
can be declared inline as the value for this attribute.
|
||||
<p>If the attribute is declared both on the application and the activity level, the value
|
||||
on the activity level takes precedence. -->
|
||||
<attr name="knownActivityEmbeddingCerts" format="reference|string" />
|
||||
|
||||
<!-- Indicate that the activity can be embedded by untrusted hosts. In this case the
|
||||
interactions and visibility of the embedded activity may be limited.
|
||||
<p>The default value of this attribute is <code>false</code>. -->
|
||||
<attr name="allowUntrustedActivityEmbedding" format="boolean" />
|
||||
|
||||
<!-- Specifies whether this {@link android.app.Activity} should be shown on
|
||||
top of the lock screen whenever the lockscreen is up and this activity has another
|
||||
activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
|
||||
@@ -2011,6 +2029,7 @@
|
||||
when the application's user data is cleared. The default value is false.
|
||||
-->
|
||||
<attr name="resetEnabledSettingsOnAppDataCleared" format="boolean" />
|
||||
<attr name="knownActivityEmbeddingCerts" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- An attribution is a logical part of an app and is identified by a tag.
|
||||
@@ -3033,6 +3052,8 @@
|
||||
<!-- Indicates whether the activity can be displayed on a remote device which may or
|
||||
may not be running Android. -->
|
||||
<attr name="canDisplayOnRemoteDevices" format="boolean"/>
|
||||
<attr name="allowUntrustedActivityEmbedding" />
|
||||
<attr name="knownActivityEmbeddingCerts" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- The <code>activity-alias</code> tag declares a new
|
||||
@@ -3073,6 +3094,8 @@
|
||||
<attr name="exported" />
|
||||
<attr name="parentActivityName" />
|
||||
<attr name="attributionTags" />
|
||||
<attr name="allowUntrustedActivityEmbedding" />
|
||||
<attr name="knownActivityEmbeddingCerts" />
|
||||
</declare-styleable>
|
||||
|
||||
<!-- The <code>meta-data</code> tag is used to attach additional
|
||||
|
||||
@@ -3276,6 +3276,8 @@
|
||||
<public name="toExtendBottom" />
|
||||
<public name="tileService" />
|
||||
<public name="windowSplashScreenBehavior" />
|
||||
<public name="allowUntrustedActivityEmbedding" />
|
||||
<public name="knownActivityEmbeddingCerts" />
|
||||
</staging-public-group>
|
||||
|
||||
<staging-public-group type="id" first-id="0x01de0000">
|
||||
|
||||
@@ -924,6 +924,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
* Checks if an activity is embedded and its presentation is customized by a
|
||||
* {@link android.window.TaskFragmentOrganizer} to only occupy a portion of Task bounds.
|
||||
*/
|
||||
@Override
|
||||
public boolean isActivityEmbedded(@NonNull Activity activity) {
|
||||
return mPresenter.isActivityEmbedded(activity.getActivityToken());
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.ActivityInfo;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** @hide **/
|
||||
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
|
||||
public interface ParsedActivity extends ParsedMainComponent {
|
||||
@@ -59,6 +61,12 @@ public interface ParsedActivity extends ParsedMainComponent {
|
||||
@Nullable
|
||||
String getPermission();
|
||||
|
||||
/**
|
||||
* Gets the trusted host certificates of apps that are allowed to embed this activity.
|
||||
*/
|
||||
@NonNull
|
||||
Set<String> getKnownActivityEmbeddingCerts();
|
||||
|
||||
int getPersistableMode();
|
||||
|
||||
int getPrivateFlags();
|
||||
|
||||
@@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
|
||||
|
||||
import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
|
||||
import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForStringSet;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -33,12 +34,17 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.DataClass;
|
||||
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
|
||||
import com.android.server.pm.pkg.parsing.ParsingUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
**/
|
||||
@@ -63,6 +69,8 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
@Nullable
|
||||
@DataClass.ParcelWith(ForInternedString.class)
|
||||
private String permission;
|
||||
@Nullable
|
||||
private Set<String> mKnownActivityEmbeddingCerts;
|
||||
|
||||
private int launchMode;
|
||||
private int documentLaunchMode;
|
||||
@@ -113,6 +121,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
this.rotationAnimation = other.rotationAnimation;
|
||||
this.colorMode = other.colorMode;
|
||||
this.windowLayout = other.windowLayout;
|
||||
this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,6 +248,25 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Set<String> getKnownActivityEmbeddingCerts() {
|
||||
return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
|
||||
: mKnownActivityEmbeddingCerts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the trusted host certificates of apps that are allowed to embed this activity.
|
||||
*/
|
||||
public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
|
||||
// Convert the provided digest to upper case for consistent Set membership
|
||||
// checks when verifying the signing certificate digests of requesting apps.
|
||||
this.mKnownActivityEmbeddingCerts = new ArraySet<>();
|
||||
for (String knownCert : knownActivityEmbeddingCerts) {
|
||||
this.mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
sb.append("Activity{");
|
||||
@@ -287,6 +315,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
} else {
|
||||
dest.writeBoolean(false);
|
||||
}
|
||||
sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
|
||||
}
|
||||
|
||||
public ParsedActivityImpl() {
|
||||
@@ -320,6 +349,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
if (in.readBoolean()) {
|
||||
windowLayout = new ActivityInfo.WindowLayout(in);
|
||||
}
|
||||
this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -344,7 +374,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
// CHECKSTYLE:OFF Generated code
|
||||
//
|
||||
// To regenerate run:
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
|
||||
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
|
||||
//
|
||||
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
|
||||
// Settings > Editor > Code Style > Formatter Control
|
||||
@@ -360,6 +390,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
@Nullable String taskAffinity,
|
||||
int privateFlags,
|
||||
@Nullable String permission,
|
||||
@Nullable Set<String> knownActivityEmbeddingCerts,
|
||||
int launchMode,
|
||||
int documentLaunchMode,
|
||||
int maxRecents,
|
||||
@@ -383,6 +414,7 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
this.taskAffinity = taskAffinity;
|
||||
this.privateFlags = privateFlags;
|
||||
this.permission = permission;
|
||||
this.mKnownActivityEmbeddingCerts = knownActivityEmbeddingCerts;
|
||||
this.launchMode = launchMode;
|
||||
this.documentLaunchMode = documentLaunchMode;
|
||||
this.maxRecents = maxRecents;
|
||||
@@ -645,10 +677,10 @@ public class ParsedActivityImpl extends ParsedMainComponentImpl implements Parse
|
||||
}
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1641431949361L,
|
||||
time = 1644372875433L,
|
||||
codegenVersion = "1.0.23",
|
||||
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/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 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<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic android.content.pm.parsing.component.ParsedActivityImpl setPermission(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 android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
|
||||
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)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
|
||||
import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
|
||||
import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
|
||||
import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -151,7 +152,8 @@ public class ParsedActivityUtils {
|
||||
| flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
|
||||
| flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
|
||||
| flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
|
||||
| flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
|
||||
| flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa))
|
||||
| flag(ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING, R.styleable.AndroidManifestActivity_allowUntrustedActivityEmbedding, sa));
|
||||
|
||||
activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
|
||||
R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
|
||||
@@ -338,6 +340,20 @@ public class ParsedActivityUtils {
|
||||
activity.setPermission(permission != null ? permission : pkg.getPermission());
|
||||
}
|
||||
|
||||
final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
|
||||
parseKnownActivityEmbeddingCerts(array, resources, isAlias
|
||||
? R.styleable.AndroidManifestActivityAlias_knownActivityEmbeddingCerts
|
||||
: R.styleable.AndroidManifestActivity_knownActivityEmbeddingCerts, input);
|
||||
if (knownActivityEmbeddingCertsResult.isError()) {
|
||||
return input.error(knownActivityEmbeddingCertsResult);
|
||||
} else {
|
||||
final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
|
||||
.getResult();
|
||||
if (knownActivityEmbeddingCerts != null) {
|
||||
activity.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean setExported = array.hasValue(exportedAttr);
|
||||
if (setExported) {
|
||||
activity.setExported(array.getBoolean(exportedAttr, false));
|
||||
|
||||
@@ -549,6 +549,7 @@ public class PackageInfoWithoutStateUtils {
|
||||
ai.metaData = a.getMetaData();
|
||||
}
|
||||
ai.applicationInfo = applicationInfo;
|
||||
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
|
||||
return ai;
|
||||
}
|
||||
|
||||
|
||||
@@ -381,6 +381,12 @@ public interface ParsingPackage extends ParsingPackageRead {
|
||||
|
||||
ParsingPackage setLocaleConfigRes(int localeConfigRes);
|
||||
|
||||
/**
|
||||
* Sets the trusted host certificates of apps that are allowed to embed activities of this
|
||||
* application.
|
||||
*/
|
||||
ParsingPackage setKnownActivityEmbeddingCerts(Set<String> knownActivityEmbeddingCerts);
|
||||
|
||||
// TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
|
||||
// for moving to the next step
|
||||
@CallSuper
|
||||
|
||||
@@ -563,6 +563,9 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
return (mBooleans & flag) != 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Set<String> mKnownActivityEmbeddingCerts;
|
||||
|
||||
// Derived fields
|
||||
@NonNull
|
||||
private UUID mStorageUuid;
|
||||
@@ -1150,6 +1153,9 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
appInfo.setVersionCode(mLongVersionCode);
|
||||
appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
|
||||
appInfo.setLocaleConfigRes(mLocaleConfigRes);
|
||||
if (mKnownActivityEmbeddingCerts != null) {
|
||||
appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
|
||||
}
|
||||
|
||||
return appInfo;
|
||||
}
|
||||
@@ -1329,6 +1335,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
dest.writeInt(this.nativeHeapZeroInitialized);
|
||||
sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
|
||||
dest.writeInt(this.mLocaleConfigRes);
|
||||
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
|
||||
}
|
||||
|
||||
public ParsingPackageImpl(Parcel in) {
|
||||
@@ -1477,6 +1484,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
this.nativeHeapZeroInitialized = in.readInt();
|
||||
this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
|
||||
this.mLocaleConfigRes = in.readInt();
|
||||
this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
|
||||
assignDerivedFields();
|
||||
}
|
||||
|
||||
@@ -2376,6 +2384,13 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
return getBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Set<String> getKnownActivityEmbeddingCerts() {
|
||||
return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
|
||||
: mKnownActivityEmbeddingCerts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInheritKeyStoreKeys() {
|
||||
return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
|
||||
@@ -2973,4 +2988,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden,
|
||||
mLocaleConfigRes = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParsingPackage setKnownActivityEmbeddingCerts(
|
||||
@Nullable Set<String> knownEmbeddingCerts) {
|
||||
mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import static android.os.Build.VERSION_CODES.DONUT;
|
||||
import static android.os.Build.VERSION_CODES.O;
|
||||
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
|
||||
|
||||
import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
|
||||
|
||||
import android.annotation.AnyRes;
|
||||
import android.annotation.CheckResult;
|
||||
import android.annotation.IntDef;
|
||||
@@ -2009,6 +2011,19 @@ public class ParsingPackageUtils {
|
||||
.AndroidManifestApplication_requestForegroundServiceExemption,
|
||||
false));
|
||||
}
|
||||
final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
|
||||
parseKnownActivityEmbeddingCerts(sa, res,
|
||||
R.styleable.AndroidManifestApplication_knownActivityEmbeddingCerts,
|
||||
input);
|
||||
if (knownActivityEmbeddingCertsResult.isError()) {
|
||||
return input.error(knownActivityEmbeddingCertsResult);
|
||||
} else {
|
||||
final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
|
||||
.getResult();
|
||||
if (knownActivityEmbeddingCerts != null) {
|
||||
pkg.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
sa.recycle();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.parsing.result.ParseInput;
|
||||
import android.content.pm.parsing.result.ParseResult;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@@ -38,6 +40,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** @hide **/
|
||||
public class ParsingUtils {
|
||||
@@ -168,4 +171,50 @@ public class ParsingUtils {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the {@link android.R.attr#knownActivityEmbeddingCerts} attribute, if available.
|
||||
*/
|
||||
@NonNull
|
||||
public static ParseResult<Set<String>> parseKnownActivityEmbeddingCerts(@NonNull TypedArray sa,
|
||||
@NonNull Resources res, int resourceId, @NonNull ParseInput input) {
|
||||
if (!sa.hasValue(resourceId)) {
|
||||
return input.success(null);
|
||||
}
|
||||
|
||||
final int knownActivityEmbeddingCertsResource = sa.getResourceId(resourceId, 0);
|
||||
if (knownActivityEmbeddingCertsResource != 0) {
|
||||
// The knownCerts attribute supports both a string array resource as well as a
|
||||
// string resource for the case where the permission should only be granted to a
|
||||
// single known signer.
|
||||
Set<String> knownEmbeddingCertificates = null;
|
||||
final String resourceType = res.getResourceTypeName(
|
||||
knownActivityEmbeddingCertsResource);
|
||||
if (resourceType.equals("array")) {
|
||||
final String[] knownCerts = res.getStringArray(knownActivityEmbeddingCertsResource);
|
||||
if (knownCerts != null) {
|
||||
knownEmbeddingCertificates = Set.of(knownCerts);
|
||||
}
|
||||
} else {
|
||||
final String knownCert = res.getString(knownActivityEmbeddingCertsResource);
|
||||
if (knownCert != null) {
|
||||
knownEmbeddingCertificates = Set.of(knownCert);
|
||||
}
|
||||
}
|
||||
if (knownEmbeddingCertificates == null || knownEmbeddingCertificates.isEmpty()) {
|
||||
return input.error("Defined a knownActivityEmbeddingCerts attribute but the "
|
||||
+ "provided resource is null");
|
||||
}
|
||||
return input.success(knownEmbeddingCertificates);
|
||||
}
|
||||
|
||||
// If the knownCerts resource ID is null - the app specified a string value for the
|
||||
// attribute representing a single trusted signer.
|
||||
final String knownCert = sa.getString(resourceId);
|
||||
if (knownCert == null || knownCert.isEmpty()) {
|
||||
return input.error("Defined a knownActivityEmbeddingCerts attribute but the provided "
|
||||
+ "string is empty");
|
||||
}
|
||||
return input.success(Set.of(knownCert));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.annotation.Nullable;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Container for fields that are eventually exposed through {@link ApplicationInfo}.
|
||||
* <p>
|
||||
@@ -284,6 +286,13 @@ public interface PkgWithoutStateAppInfo {
|
||||
@Nullable
|
||||
String getPermission();
|
||||
|
||||
/**
|
||||
* @see ApplicationInfo#knownActivityEmbeddingCerts
|
||||
* @see R.styleable#AndroidManifestApplication_knownActivityEmbeddingCerts
|
||||
*/
|
||||
@NonNull
|
||||
Set<String> getKnownActivityEmbeddingCerts();
|
||||
|
||||
/**
|
||||
* @see ApplicationInfo#processName
|
||||
* @see R.styleable#AndroidManifestApplication_process
|
||||
|
||||
@@ -2003,8 +2003,8 @@ class ActivityStarter {
|
||||
* @param targetTask the target task for launching activity, which could be different from
|
||||
* the one who hosting the embedding.
|
||||
*/
|
||||
private boolean canEmbedActivity(@NonNull TaskFragment taskFragment, ActivityRecord starting,
|
||||
boolean newTask, Task targetTask) {
|
||||
private boolean canEmbedActivity(@NonNull TaskFragment taskFragment,
|
||||
@NonNull ActivityRecord starting, boolean newTask, Task targetTask) {
|
||||
final Task hostTask = taskFragment.getTask();
|
||||
if (hostTask == null) {
|
||||
return false;
|
||||
@@ -2016,8 +2016,7 @@ class ActivityStarter {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Not allowed embedding an activity of another app.
|
||||
if (hostUid != starting.getUid()) {
|
||||
if (!taskFragment.isAllowedToEmbedActivity(starting)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
|
||||
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
|
||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
@@ -95,6 +96,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.internal.util.function.pooled.PooledPredicate;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -102,6 +104,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -504,6 +507,56 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the organized task fragment is allowed to have the specified activity, which is
|
||||
* allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
|
||||
* enabled.
|
||||
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
|
||||
*/
|
||||
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
|
||||
if ((a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
|
||||
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isAllowedToEmbedActivityInTrustedMode(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the organized task fragment is allowed to embed activity in fully trusted mode,
|
||||
* which means that all transactions are allowed. This is supported in the following cases:
|
||||
* <li>the activity belongs to the same app as the organizer host;</li>
|
||||
* <li>the activity has declared the organizer host as trusted explicitly via known
|
||||
* certificate.</li>
|
||||
*/
|
||||
private boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
|
||||
if (mTaskFragmentOrganizerUid == a.getUid()) {
|
||||
// Activities from the same UID can be embedded freely by the host.
|
||||
return true;
|
||||
}
|
||||
|
||||
Set<String> knownActivityEmbeddingCerts = a.info.getKnownActivityEmbeddingCerts();
|
||||
if (knownActivityEmbeddingCerts.isEmpty()) {
|
||||
// An application must either declare that it allows untrusted embedding, or specify
|
||||
// a set of app certificates that are allowed to embed it in trusted mode.
|
||||
return false;
|
||||
}
|
||||
|
||||
AndroidPackage hostPackage = mAtmService.getPackageManagerInternalLocked()
|
||||
.getPackage(mTaskFragmentOrganizerUid);
|
||||
|
||||
return hostPackage != null && hostPackage.getSigningDetails().hasAncestorOrSelfWithDigest(
|
||||
knownActivityEmbeddingCerts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all activities in the task fragment are allowed to be embedded in trusted mode.
|
||||
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
|
||||
*/
|
||||
boolean isAllowedToBeEmbeddedInTrustedMode() {
|
||||
return forAllActivities(this::isAllowedToEmbedActivityInTrustedMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the TaskFragment that is being organized, which could be this or the ascendant
|
||||
* TaskFragment.
|
||||
|
||||
@@ -294,14 +294,28 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. */
|
||||
/**
|
||||
* Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
|
||||
* {@code null} if it doesn't, or if the organizer has activity(ies) embedded in untrusted mode.
|
||||
*/
|
||||
@Nullable
|
||||
public RemoteAnimationDefinition getRemoteAnimationDefinition(
|
||||
ITaskFragmentOrganizer organizer) {
|
||||
synchronized (mGlobalLock) {
|
||||
final TaskFragmentOrganizerState organizerState =
|
||||
mTaskFragmentOrganizerState.get(organizer.asBinder());
|
||||
return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
|
||||
if (organizerState == null) {
|
||||
return null;
|
||||
}
|
||||
for (TaskFragment tf : organizerState.mOrganizedTaskFragments) {
|
||||
if (!tf.isAllowedToBeEmbeddedInTrustedMode()) {
|
||||
// Disable client-driven animations for organizer if at least one of the
|
||||
// embedded task fragments is not embedding in trusted mode.
|
||||
// TODO(b/197364677): replace with a stub or Shell-driven one instead of skip?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return organizerState.mRemoteAnimationDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.server.wm;
|
||||
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
|
||||
import static android.app.ActivityManager.isStartResultSuccessful;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION;
|
||||
import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION_RECT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
|
||||
@@ -1224,10 +1226,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
while (entries.hasNext()) {
|
||||
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
|
||||
// Only allow to apply changes to TaskFragment that is created by this organizer.
|
||||
enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
|
||||
organizer);
|
||||
WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
|
||||
enforceTaskFragmentOrganized(func, wc, organizer);
|
||||
enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer);
|
||||
}
|
||||
|
||||
// TODO(b/197364677): Enforce safety of hierarchy operations in untrusted mode. E.g. one
|
||||
// could first change a trusted TF, and then start/reparent untrusted activity there.
|
||||
|
||||
// Hierarchy changes
|
||||
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
|
||||
for (int i = hops.size() - 1; i >= 0; i--) {
|
||||
@@ -1297,6 +1303,57 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that SurfaceControl transactions and the ability to set bounds outside of the
|
||||
* parent bounds are not allowed for embedding without full trust between the host and the
|
||||
* target.
|
||||
* TODO(b/197364677): Allow SC transactions when the client-driven animations are protected from
|
||||
* tapjacking.
|
||||
*/
|
||||
private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
|
||||
WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) {
|
||||
if (wc == null) {
|
||||
Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
|
||||
return;
|
||||
}
|
||||
// Check if TaskFragment is embedded in fully trusted mode
|
||||
if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
|
||||
// Fully trusted, no need to check further
|
||||
return;
|
||||
}
|
||||
|
||||
if (change == null) {
|
||||
return;
|
||||
}
|
||||
final int changeMask = change.getChangeMask();
|
||||
if ((changeMask & (CHANGE_BOUNDS_TRANSACTION | CHANGE_BOUNDS_TRANSACTION_RECT)) != 0) {
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply SurfaceControl changes to TaskFragment in non-trusted "
|
||||
+ "embedding mode, TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
if (change.getWindowSetMask() == 0) {
|
||||
// Nothing else to check.
|
||||
return;
|
||||
}
|
||||
WindowConfiguration requestedWindowConfig = change.getConfiguration().windowConfiguration;
|
||||
WindowContainer wcParent = wc.getParent();
|
||||
if (wcParent == null) {
|
||||
Slog.e(TAG, "Attempt to set bounds on task fragment that has no parent");
|
||||
return;
|
||||
}
|
||||
if (!wcParent.getBounds().contains(requestedWindowConfig.getBounds())) {
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply bounds outside of parent for non-trusted host,"
|
||||
+ " TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
|
||||
@Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
|
||||
final ActivityRecord ownerActivity =
|
||||
|
||||
@@ -508,7 +508,8 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
|
||||
AndroidPackage::shouldInheritKeyStoreKeys,
|
||||
ParsingPackage::setInheritKeyStoreKeys,
|
||||
true
|
||||
)
|
||||
),
|
||||
getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
|
||||
)
|
||||
|
||||
override fun initialObject() = PackageImpl.forParsing(
|
||||
|
||||
@@ -53,7 +53,7 @@ class ParsedActivityTest : ParsedMainComponentTest(
|
||||
ParsedActivity::getTaskAffinity,
|
||||
ParsedActivity::getTheme,
|
||||
ParsedActivity::getUiOptions,
|
||||
ParsedActivity::isSupportsSizeChanges,
|
||||
ParsedActivity::isSupportsSizeChanges
|
||||
)
|
||||
|
||||
override fun mainComponentSubclassExtraParams() = listOf(
|
||||
@@ -73,6 +73,7 @@ class ParsedActivityTest : ParsedMainComponentTest(
|
||||
ActivityInfo.WindowLayout::minHeight
|
||||
)
|
||||
}
|
||||
)
|
||||
),
|
||||
getter(ParsedActivity::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -895,6 +895,7 @@ public class PackageParserTest {
|
||||
assertEquals(a.networkSecurityConfigRes, that.networkSecurityConfigRes);
|
||||
assertEquals(a.taskAffinity, that.taskAffinity);
|
||||
assertEquals(a.permission, that.permission);
|
||||
assertEquals(a.getKnownActivityEmbeddingCerts(), that.getKnownActivityEmbeddingCerts());
|
||||
assertEquals(a.processName, that.processName);
|
||||
assertEquals(a.className, that.className);
|
||||
assertEquals(a.manageSpaceActivityName, that.manageSpaceActivityName);
|
||||
|
||||
@@ -75,6 +75,7 @@ import android.content.pm.ActivityInfo.WindowLayout;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.SigningDetails;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
@@ -84,9 +85,11 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.service.voice.IVoiceInteractionSession;
|
||||
import android.util.Pair;
|
||||
import android.view.Gravity;
|
||||
import android.window.TaskFragmentOrganizerToken;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
|
||||
import com.android.server.wm.utils.MockTracker;
|
||||
|
||||
@@ -94,6 +97,10 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for the {@link ActivityStarter} class.
|
||||
*
|
||||
@@ -1109,7 +1116,7 @@ public class ActivityStarterTests extends WindowTestsBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartActivityInner_inTaskFragment() {
|
||||
public void testStartActivityInner_inTaskFragment_failsByDefault() {
|
||||
final ActivityStarter starter = prepareStarter(0, false);
|
||||
final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
|
||||
final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
@@ -1117,6 +1124,97 @@ public class ActivityStarterTests extends WindowTestsBase {
|
||||
true /* createdByOrganizer */);
|
||||
sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
|
||||
|
||||
starter.startActivityInner(
|
||||
/* r */targetRecord,
|
||||
/* sourceRecord */ sourceRecord,
|
||||
/* voiceSession */null,
|
||||
/* voiceInteractor */ null,
|
||||
/* startFlags */ 0,
|
||||
/* doResume */true,
|
||||
/* options */null,
|
||||
/* inTask */null,
|
||||
/* inTaskFragment */ taskFragment,
|
||||
/* restrictedBgActivity */false,
|
||||
/* intentGrants */null);
|
||||
|
||||
assertFalse(taskFragment.hasChild());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartActivityInner_inTaskFragment_allowedForSameUid() {
|
||||
final ActivityStarter starter = prepareStarter(0, false);
|
||||
final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
|
||||
final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
|
||||
true /* createdByOrganizer */);
|
||||
sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
|
||||
|
||||
taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
|
||||
targetRecord.getUid(), "test_process_name");
|
||||
|
||||
starter.startActivityInner(
|
||||
/* r */targetRecord,
|
||||
/* sourceRecord */ sourceRecord,
|
||||
/* voiceSession */null,
|
||||
/* voiceInteractor */ null,
|
||||
/* startFlags */ 0,
|
||||
/* doResume */true,
|
||||
/* options */null,
|
||||
/* inTask */null,
|
||||
/* inTaskFragment */ taskFragment,
|
||||
/* restrictedBgActivity */false,
|
||||
/* intentGrants */null);
|
||||
|
||||
assertTrue(taskFragment.hasChild());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartActivityInner_inTaskFragment_allowedTrustedCertUid() {
|
||||
final ActivityStarter starter = prepareStarter(0, false);
|
||||
final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
|
||||
final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
|
||||
true /* createdByOrganizer */);
|
||||
sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
|
||||
|
||||
taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
|
||||
12345, "test_process_name");
|
||||
AndroidPackage androidPackage = mock(AndroidPackage.class);
|
||||
doReturn(androidPackage).when(mMockPackageManager).getPackage(eq(12345));
|
||||
|
||||
Set<String> certs = new HashSet(Arrays.asList("test_cert1", "test_cert1"));
|
||||
targetRecord.info.setKnownActivityEmbeddingCerts(certs);
|
||||
SigningDetails signingDetails = mock(SigningDetails.class);
|
||||
doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any());
|
||||
doReturn(signingDetails).when(androidPackage).getSigningDetails();
|
||||
|
||||
starter.startActivityInner(
|
||||
/* r */targetRecord,
|
||||
/* sourceRecord */ sourceRecord,
|
||||
/* voiceSession */null,
|
||||
/* voiceInteractor */ null,
|
||||
/* startFlags */ 0,
|
||||
/* doResume */true,
|
||||
/* options */null,
|
||||
/* inTask */null,
|
||||
/* inTaskFragment */ taskFragment,
|
||||
/* restrictedBgActivity */false,
|
||||
/* intentGrants */null);
|
||||
|
||||
assertTrue(taskFragment.hasChild());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartActivityInner_inTaskFragment_allowedForUntrustedEmbedding() {
|
||||
final ActivityStarter starter = prepareStarter(0, false);
|
||||
final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
|
||||
final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
|
||||
true /* createdByOrganizer */);
|
||||
sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
|
||||
|
||||
targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
|
||||
|
||||
starter.startActivityInner(
|
||||
/* r */targetRecord,
|
||||
/* sourceRecord */ sourceRecord,
|
||||
|
||||
Reference in New Issue
Block a user