Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c32240cb authored by Nan Wu's avatar Nan Wu
Browse files

Revert^2 "Create token for all nested intents of a top level intent."

This reverts commit 8115615c.

Reason for revert: A fix for the problem has been identified.
The check creator token action has to be split into 2. First,
mark the intent as trusted creator token present. After extract
all extra intents from the intent's extra bundle, then mark
the bundle as enbable token verifiy.

Change-Id: Ibcfbe413ca4a25737a7d85d211e03e383ea7ebf1
parent ffa53334
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -398,6 +398,7 @@ public class ClipData implements Parcelable {
         * Retrieve the raw Intent contained in this Item.
         */
        public Intent getIntent() {
            Intent.maybeMarkAsMissingCreatorToken(mIntent);
            return mIntent;
        }

+265 −31
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import android.util.AttributeSet;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.expresslog.Counter;
@@ -108,6 +109,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.function.Consumer;
/**
 * An intent is an abstract description of an operation to be performed.  It
@@ -892,6 +894,20 @@ public class Intent implements Parcelable, Cloneable {
    public static void maybeMarkAsMissingCreatorToken(Object object) {
        if (object instanceof Intent intent) {
            maybeMarkAsMissingCreatorTokenInternal(intent);
        } else if (object instanceof Parcelable[] parcelables) {
            for (Parcelable p : parcelables) {
                if (p instanceof Intent intent) {
                    maybeMarkAsMissingCreatorTokenInternal(intent);
                }
            }
        } else if (object instanceof ArrayList parcelables) {
            int N = parcelables.size();
            for (int i = 0; i < N; i++) {
                Object p = parcelables.get(i);
                if (p instanceof Intent intent) {
                    maybeMarkAsMissingCreatorTokenInternal(intent);
                }
            }
        }
    }
@@ -12204,7 +12220,68 @@ public class Intent implements Parcelable, Cloneable {
        // Stores a creator token for an intent embedded as an extra intent in a top level intent,
        private IBinder mCreatorToken;
        // Stores all extra keys whose values are intents for a top level intent.
        private ArraySet<String> mExtraIntentKeys;
        private ArraySet<NestedIntentKey> mNestedIntentKeys;
    }
    /**
     * @hide
     */
    public static class NestedIntentKey {
        /** @hide */
        @IntDef(flag = true, prefix = {"NESTED_INTENT_KEY_TYPE"}, value = {
                NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL,
                NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY,
                NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST,
                NESTED_INTENT_KEY_TYPE_CLIP_DATA,
        })
        @Retention(RetentionPolicy.SOURCE)
        private @interface NestedIntentKeyType {
        }
        /**
         * This flag indicates the key is for an extra parcel in mExtras.
         */
        private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL = 1 << 0;
        /**
         * This flag indicates the key is for an extra parcel array in mExtras and the index is the
         * index of that array.
         */
        private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY = 1 << 1;
        /**
         * This flag indicates the key is for an extra parcel list in mExtras and the index is the
         * index of that list.
         */
        private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST = 1 << 2;
        /**
         * This flag indicates the key is for an extra parcel in mClipData.mItems.
         */
        private static final int NESTED_INTENT_KEY_TYPE_CLIP_DATA = 1 << 3;
        private final @NestedIntentKeyType int mType;
        private final String mKey;
        private final int mIndex;
        private NestedIntentKey(@NestedIntentKeyType int type, String key, int index) {
            this.mType = type;
            this.mKey = key;
            this.mIndex = index;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            NestedIntentKey that = (NestedIntentKey) o;
            return mType == that.mType && mIndex == that.mIndex && Objects.equals(mKey, that.mKey);
        }
        @Override
        public int hashCode() {
            return Objects.hash(mType, mKey, mIndex);
        }
    }
    private @Nullable CreatorTokenInfo mCreatorTokenInfo;
@@ -12227,8 +12304,9 @@ public class Intent implements Parcelable, Cloneable {
    }
    /** @hide */
    public Set<String> getExtraIntentKeys() {
        return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys;
    @VisibleForTesting
    public Set<NestedIntentKey> getExtraIntentKeys() {
        return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mNestedIntentKeys;
    }
    /** @hide */
@@ -12246,45 +12324,178 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    public void collectExtraIntentKeys() {
        if (!preventIntentRedirect()) return;
        if (preventIntentRedirect()) {
            collectNestedIntentKeysRecur(new ArraySet<>());
        }
    }
        if (mExtras != null && !mExtras.isEmpty()) {
    private void collectNestedIntentKeysRecur(Set<Intent> visited) {
        if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
            for (String key : mExtras.keySet()) {
                if (mExtras.get(key) instanceof Intent) {
                Object value = mExtras.get(key);
                if (value instanceof Intent intent && !visited.contains(intent)) {
                    handleNestedIntent(intent, visited, new NestedIntentKey(
                            NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0));
                } else if (value instanceof Parcelable[] parcelables) {
                    handleParcelableArray(parcelables, key, visited);
                } else if (value instanceof ArrayList<?> parcelables) {
                    handleParcelableList(parcelables, key, visited);
                }
            }
        }
        if (mClipData != null) {
            for (int i = 0; i < mClipData.getItemCount(); i++) {
                Intent intent = mClipData.getItemAt(i).mIntent;
                if (intent != null && !visited.contains(intent)) {
                    handleNestedIntent(intent, visited, new NestedIntentKey(
                            NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA, null, i));
                }
            }
        }
    }
    private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key) {
        visited.add(intent);
        if (mCreatorTokenInfo == null) {
            mCreatorTokenInfo = new CreatorTokenInfo();
        }
                    if (mCreatorTokenInfo.mExtraIntentKeys == null) {
                        mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>();
        if (mCreatorTokenInfo.mNestedIntentKeys == null) {
            mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>();
        }
                    mCreatorTokenInfo.mExtraIntentKeys.add(key);
        mCreatorTokenInfo.mNestedIntentKeys.add(key);
        intent.collectNestedIntentKeysRecur(visited);
    }
    private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited) {
        for (int i = 0; i < parcelables.length; i++) {
            if (parcelables[i] instanceof Intent intent && !visited.contains(intent)) {
                handleNestedIntent(intent, visited, new NestedIntentKey(
                        NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, key, i));
            }
        }
    }
    /** @hide */
    public void checkCreatorToken() {
        if (mExtras == null) return;
        if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) {
            for (String key : mCreatorTokenInfo.mExtraIntentKeys) {
                try {
                    Intent extraIntent = mExtras.getParcelable(key, Intent.class);
                    if (extraIntent == null) {
                        Log.w(TAG, "The key {" + key
                                + "} does not correspond to an intent in the bundle.");
                        continue;
    private void handleParcelableList(ArrayList<?> parcelables, String key, Set<Intent> visited) {
        for (int i = 0; i < parcelables.size(); i++) {
            if (parcelables.get(i) instanceof Intent intent && !visited.contains(intent)) {
                handleNestedIntent(intent, visited, new NestedIntentKey(
                        NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, key, i));
            }
                    extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
                } catch (Exception e) {
                    Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e);
        }
    }
    private static final Consumer<Intent> MARK_TRUSTED_TOKEN_PRESENT_ACTION = intent -> {
        intent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT;
    };
    private static final Consumer<Intent> ENABLE_TOKEN_VERIFY_ACTION = intent -> {
        if (intent.mExtras != null) {
            intent.mExtras.enableTokenVerification();
        }
    };
    /** @hide */
    public void checkCreatorToken() {
        forEachNestedCreatorToken(MARK_TRUSTED_TOKEN_PRESENT_ACTION, ENABLE_TOKEN_VERIFY_ACTION);
        if (mExtras != null) {
            // mark the bundle as intent extras after calls to getParcelable.
            // otherwise, the logic to mark missing token would run before
            // mark trusted creator token present.
        mExtras.setIsIntentExtra();
            mExtras.enableTokenVerification();
        }
    }
    /** @hide */
    public void forEachNestedCreatorToken(Consumer<? super Intent> action) {
        forEachNestedCreatorToken(action, null);
    }
    private void forEachNestedCreatorToken(Consumer<? super Intent> action,
            Consumer<? super Intent> postAction) {
        if (mExtras == null && mClipData == null) return;
        if (mCreatorTokenInfo != null && mCreatorTokenInfo.mNestedIntentKeys != null) {
            int N = mCreatorTokenInfo.mNestedIntentKeys.size();
            for (int i = 0; i < N; i++) {
                NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
                Intent extraIntent = extractIntentFromKey(key);
                if (extraIntent != null) {
                    action.accept(extraIntent);
                    extraIntent.forEachNestedCreatorToken(action);
                    if (postAction != null) {
                        postAction.accept(extraIntent);
                    }
                } else {
                    Log.w(TAG, getLogMessageForKey(key));
                }
            }
        }
    }
    private Intent extractIntentFromKey(NestedIntentKey key) {
        switch (key.mType) {
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL:
                return mExtras == null ? null : mExtras.getParcelable(key.mKey, Intent.class);
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY:
                if (mExtras == null) return null;
                Intent[] extraIntents = mExtras.getParcelableArray(key.mKey, Intent.class);
                if (extraIntents != null && key.mIndex < extraIntents.length) {
                    return extraIntents[key.mIndex];
                }
                break;
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST:
                if (mExtras == null) return null;
                ArrayList<Intent> extraIntentsList = mExtras.getParcelableArrayList(key.mKey,
                        Intent.class);
                if (extraIntentsList != null && key.mIndex < extraIntentsList.size()) {
                    return extraIntentsList.get(key.mIndex);
                }
                break;
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA:
                if (mClipData == null) return null;
                if (key.mIndex < mClipData.getItemCount()) {
                    ClipData.Item item = mClipData.getItemAt(key.mIndex);
                    if (item != null) {
                        return item.mIntent;
                    }
                }
                break;
        }
        return null;
    }
    private String getLogMessageForKey(NestedIntentKey key) {
        switch (key.mType) {
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL:
                return "The key {" + key + "} does not correspond to an intent in the bundle.";
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY:
                if (mExtras.getParcelableArray(key.mKey, Intent.class) == null) {
                    return "The key {" + key
                            + "} does not correspond to a Parcelable[] in the bundle.";
                } else {
                    return "Parcelable[" + key.mIndex + "] for key {" + key + "} is not an intent.";
                }
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST:
                if (mExtras.getParcelableArrayList(key.mKey, Intent.class) == null) {
                    return "The key {" + key
                            + "} does not correspond to an ArrayList<Parcelable> in the bundle.";
                } else {
                    return "List.get(" + key.mIndex + ") for key {" + key + "} is not an intent.";
                }
            case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA:
                if (key.mIndex >= mClipData.getItemCount()) {
                    return "Index out of range for clipData items. index: " + key.mIndex
                            + ". item counts: " + mClipData.getItemCount();
                } else {
                    return "clipData items at index [" + key.mIndex
                            + "] is null or does not contain an intent.";
                }
            default:
                return "Unknown key type: " + key.mType;
        }
    }
    /**
@@ -12357,7 +12568,19 @@ public class Intent implements Parcelable, Cloneable {
            } else {
                out.writeInt(1);
                out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken);
                out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys);
                if (mCreatorTokenInfo.mNestedIntentKeys != null) {
                    final int N = mCreatorTokenInfo.mNestedIntentKeys.size();
                    out.writeInt(N);
                    for (int i = 0; i < N; i++) {
                        NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
                        out.writeInt(key.mType);
                        out.writeString8(key.mKey);
                        out.writeInt(key.mIndex);
                    }
                } else {
                    out.writeInt(0);
                }
            }
        }
    }
@@ -12422,7 +12645,18 @@ public class Intent implements Parcelable, Cloneable {
            if (in.readInt() != 0) {
                mCreatorTokenInfo = new CreatorTokenInfo();
                mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
                mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null);
                N = in.readInt();
                if (N > 0) {
                    mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(N);
                    for (int i = 0; i < N; i++) {
                        int type = in.readInt();
                        String key = in.readString8();
                        int index = in.readInt();
                        mCreatorTokenInfo.mNestedIntentKeys.append(
                                new NestedIntentKey(type, key, index));
                    }
                }
            }
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -281,7 +281,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
    }

    /** {@hide} */
    public void setIsIntentExtra() {
    public void enableTokenVerification() {
        mFlags |= FLAG_VERIFY_TOKENS_PRESENT;
    }

+38 −5
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *  Build/Install/Run:
 *   atest FrameworksCoreTests:IntentTest
@@ -57,7 +61,12 @@ public class IntentTest {
    public void testReadFromParcelWithExtraIntentKeys() {
        Intent intent = new Intent("TEST_ACTION");
        intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION));
        // Not an intent, don't count.
        intent.putExtra(TEST_EXTRA_NAME + "2", 1);
        ArrayList<Intent> intents = new ArrayList<>();
        intents.add(new Intent(TEST_ACTION));
        intent.putParcelableArrayListExtra(TEST_EXTRA_NAME + "3", intents);
        intent.setClipData(ClipData.newIntent("label", new Intent(TEST_ACTION)));

        intent.collectExtraIntentKeys();
        final Parcel parcel = Parcel.obtain();
@@ -68,7 +77,7 @@ public class IntentTest {

        assertEquals(intent.getAction(), target.getAction());
        assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys());
        assertThat(intent.getExtraIntentKeys()).hasSize(1);
        assertThat(intent.getExtraIntentKeys()).hasSize(3);
    }

    @Test
@@ -87,13 +96,37 @@ public class IntentTest {
    @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT)
    public void testCollectExtraIntentKeys() {
        Intent intent = new Intent(TEST_ACTION);
        Intent extraIntent = new Intent(TEST_ACTION, TEST_URI);
        intent.putExtra(TEST_EXTRA_NAME, extraIntent);

        Intent[] intents = new Intent[10];
        for (int i = 0; i < intents.length; i++) {
            intents[i] = new Intent("action" + i);
        }
        Intent[] intents2 = new Intent[2]; // intents[6-7]
        System.arraycopy(intents, 6, intents2, 0, intents2.length);
        ArrayList<Intent> intents3 = new ArrayList<>(2);
        intents3.addAll(Arrays.asList(intents).subList(8, 10)); // intents[8-9]
        intent.putExtra("key1", intents[0]);
        intent.putExtra("array-key", intents2);
        intent.setClipData(ClipData.newIntent("label2", intents[1]));
        intent.putExtra("intkey", 1);
        intents[0].putExtra("key3", intents[2]);
        intents[0].setClipData(ClipData.newIntent("label4", intents[3]));
        intents[0].putParcelableArrayListExtra("array-list-key", intents3);
        intents[1].putExtra("key3", intents[4]);
        intents[1].setClipData(ClipData.newIntent("label4", intents[5]));
        intents[5].putExtra("intkey", 2);

        intent.collectExtraIntentKeys();

        assertThat(intent.getExtraIntentKeys()).hasSize(1);
        assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME);
        // collect all actions of nested intents.
        final List<String> actions = new ArrayList<>();
        intent.forEachNestedCreatorToken(intent1 -> {
            actions.add(intent1.getAction());
        });
        assertThat(actions).hasSize(10);
        for (int i = 0; i < intents.length; i++) {
            assertThat(actions).contains("action" + i);
        }
    }

}
+11 −24
Original line number Diff line number Diff line
@@ -19307,31 +19307,18 @@ public class ActivityManagerService extends IActivityManager.Stub
    public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
        if (!preventIntentRedirect()) return;
        if (intent == null || intent.getExtraIntentKeys() == null) return;
        for (String key : intent.getExtraIntentKeys()) {
            try {
                Intent extraIntent = intent.getParcelableExtra(key, Intent.class);
                if (extraIntent == null) {
                    Slog.w(TAG, "The key {" + key
                            + "} does not correspond to an intent in the extra bundle.");
                    continue;
                }
                IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent,
                        creatorPackage);
        if (intent == null) return;
        intent.forEachNestedCreatorToken(extraIntent -> {
            IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
            if (creatorToken != null) {
                extraIntent.setCreatorToken(creatorToken);
                // TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329
                Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: "
                            + creatorPackage + "; intent: " + intent);
                        + creatorPackage + "; intent: " + extraIntent);
                FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED,
                        creatorToken.getCreatorUid());
            }
            } catch (Exception e) {
                Slog.wtf(TAG,
                        "Something went wrong when trying to add creator token for embedded "
                                + "intents of intent: ."
                                + intent, e);
            }
        }
        });
    }
    private IntentCreatorToken createIntentCreatorToken(Intent intent, String creatorPackage) {
Loading