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

Commit 20b20908 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Bug fix + clean up on component alias"

parents c103afef b7e9674d
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -3211,7 +3211,7 @@ public final class ActiveServices {
        // See if the intent refers to an alias. If so, update the intent with the target component
        // name. `resolution` will contain the alias component name, which we need to return
        // to the client.
        final ComponentAliasResolver.Resolution resolution =
        final ComponentAliasResolver.Resolution<ComponentName> resolution =
                mAm.mComponentAliasResolver.resolveService(service, resolvedType,
                        /* match flags */ 0, userId, callingUid);

@@ -3448,7 +3448,7 @@ public final class ActiveServices {
                    return null;
                }
            }
            return new ServiceLookupResult(r, resolution.getAliasComponent());
            return new ServiceLookupResult(r, resolution.getAlias());
        }
        return null;
    }
@@ -4448,6 +4448,8 @@ public final class ActiveServices {
                // being brought down.  Mark it as dead.
                cr.serviceDead = true;
                cr.stopAssociation();
                final ComponentName clientSideComponentName =
                        cr.aliasComponent != null ? cr.aliasComponent : r.name;
                try {
                    cr.conn.connected(r.name, null, true);
                } catch (Exception e) {
+15 −20
Original line number Diff line number Diff line
@@ -189,7 +189,7 @@ public class ComponentAliasResolver {
    @GuardedBy("mLock")
    private void loadOverridesLocked() {
        if (DEBUG) Slog.d(TAG, "Loading aliases overrides ...");
        for (String line : mOverrideString.split("\\+")) {
        for (String line : mOverrideString.split("\\,+")) {
            final String[] fields = line.split("\\:+", 2);
            final ComponentName from = ComponentName.unflattenFromString(fields[0]);
            if (!validateComponentName(from)) {
@@ -247,50 +247,45 @@ public class ComponentAliasResolver {
    /**
     * Contains alias resolution information.
     */
    public static class Resolution {
        @NonNull
        public final Intent sourceIntent;

    public static class Resolution<T> {
        /** "From" component. Null if component alias is disabled. */
        @Nullable
        public final ComponentName sourceComponent;
        public final T source;

        /** "To" component. Null if component alias is disabled, or the source isn't an alias. */
        @Nullable
        public final ComponentName resolvedComponent;
        public final T resolved;

        public Resolution(Intent sourceIntent,
                ComponentName sourceComponent, ComponentName resolvedComponent) {
            this.sourceIntent = sourceIntent;
            this.sourceComponent = sourceComponent;
            this.resolvedComponent = resolvedComponent;
        public Resolution(T source, T resolved) {
            this.source = source;
            this.resolved = resolved;
        }

        @Nullable
        public boolean isAlias() {
            return this.resolvedComponent != null;
            return this.resolved != null;
        }

        @Nullable
        public ComponentName getAliasComponent() {
            return isAlias() ? sourceComponent : null;
        public T getAlias() {
            return isAlias() ? source : null;
        }

        @Nullable
        public ComponentName getTargetComponent() {
            return isAlias() ? resolvedComponent : null;
        public T getTarget() {
            return isAlias() ? resolved : null;
        }
    }

    @Nullable
    public Resolution resolveService(
    public Resolution<ComponentName> resolveService(
            @NonNull Intent service, @Nullable String resolvedType,
            int packageFlags, int userId, int callingUid) {
        final long identity = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                if (!mEnabled) {
                    return new Resolution(service, null, null);
                    return new Resolution<>(null, null);
                }

                PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
@@ -317,7 +312,7 @@ public class ComponentAliasResolver {
                                + " -> " + target.flattenToShortString());
                    }
                }
                return new Resolution(service, alias, target);
                return new Resolution<>(alias, target);
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
+81 −12
Original line number Diff line number Diff line
@@ -20,10 +20,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.HandlerThread;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;

import java.util.ArrayList;

@@ -37,39 +40,83 @@ public final class BroadcastMessenger {

    private static final String ACTION_MESSAGE =
            "com.android.compatibility.common.util.BroadcastMessenger.ACTION_MESSAGE";
    private static final String ACTION_PING =
            "com.android.compatibility.common.util.BroadcastMessenger.ACTION_PING";
    private static final String EXTRA_MESSAGE =
            "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_MESSAGE";

    /** Send a message to the {@link Receiver} in a given package. */
    public static <T extends Parcelable> void send(@NonNull Context context,
            @NonNull String receiverPackage,
            @NonNull T message) {
        final Intent i = new Intent(ACTION_MESSAGE);
        i.putExtra(EXTRA_MESSAGE, message);
    /**
     * We need to drop messages that were sent before the receiver was created. We keep
     * track of the message send time in this extra.
     */
    private static final String EXTRA_SENT_TIME =
            "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_SENT_TIME";

    private static long getCurrentTime() {
        return SystemClock.uptimeMillis();
    }

    private static void sendBroadcast(@NonNull Intent i,
            @NonNull Context context, @NonNull String receiverPackage) {
        i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        i.setPackage(receiverPackage);
        i.putExtra(EXTRA_SENT_TIME, getCurrentTime());

        Log.d(TAG, "Sending: " + message);
        context.sendBroadcast(i);
    }

    /** Send a message to the {@link Receiver} in a given package. */
    public static <T extends Parcelable> void send(@NonNull Context context,
            @NonNull String receiverPackage, @NonNull T message) {
        final Intent i = new Intent(ACTION_MESSAGE);
        i.putExtra(EXTRA_MESSAGE, Preconditions.checkNotNull(message));

        Log.i(TAG, "Sending: " + message);
        sendBroadcast(i, context, receiverPackage);
    }

    private static void sendPing(@NonNull Context context, @NonNull String receiverPackage) {
        final Intent i = new Intent(ACTION_PING);

        Log.i(TAG, "Sending a ping");
        sendBroadcast(i, context, receiverPackage);
    }

    /**
     * Receive messages from the test app.
     * Receive messages sent with {@link #send}. Note it'll ignore all the messages that were
     * sent before instantiated.
     */
    public static final class Receiver<T extends Parcelable> implements AutoCloseable {
        private final Context mContext;
        private final HandlerThread mReceiverThread = new HandlerThread(TAG);

        @GuardedBy("mMessages")
        private final ArrayList<T> mMessages = new ArrayList<>();
        private final long mCreatedTime = getCurrentTime();
        private boolean mRegistered;

        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (!ACTION_MESSAGE.equals(intent.getAction())) {
                    throw new RuntimeException("Unknown message received: " + intent);
                // Log.d(TAG, "Received intent: " + intent);
                switch (intent.getAction()) {
                    case ACTION_MESSAGE:
                    case ACTION_PING:
                        break;
                    default:
                        throw new RuntimeException("Unknown broadcast received: " + intent);
                }
                if (intent.getLongExtra(EXTRA_SENT_TIME, 0) < mCreatedTime) {
                    Log.i(TAG, "Dropping stale broadcast: " + intent);
                    return;
                }

                // Note for a PING, the message will be null.
                final T message = intent.getParcelableExtra(EXTRA_MESSAGE);
                Log.d(TAG, "Received: " + message);
                if (message != null) {
                    Log.i(TAG, "Received: " + message);
                }

                synchronized (mMessages) {
                    mMessages.add(message);
                    mMessages.notifyAll();
@@ -83,8 +130,13 @@ public final class BroadcastMessenger {
        public Receiver(@NonNull Context context) {
            mContext = context;

            mReceiverThread.start();

            final IntentFilter fi = new IntentFilter(ACTION_MESSAGE);
            context.registerReceiver(mReceiver, fi);
            fi.addAction(ACTION_PING);

            context.registerReceiver(mReceiver, fi, /** permission=*/ null,
                    mReceiverThread.getThreadHandler());
            mRegistered = true;
        }

@@ -92,6 +144,7 @@ public final class BroadcastMessenger {
        public void close() {
            if (mRegistered) {
                mContext.unregisterReceiver(mReceiver);
                mReceiverThread.quit();
                mRegistered = false;
            }
        }
@@ -121,5 +174,21 @@ public final class BroadcastMessenger {
                return mMessages.remove(0);
            }
        }

        /**
         * Ensure that no further messages have been received.
         *
         * Call it before {@link #close()}.
         */
        public void ensureNoMoreMessages() throws Exception {
            // Send a ping to myself.
            sendPing(mContext, mContext.getPackageName());

            final T m = waitForNextMessage();
            if (m == null) {
                return; // Okay. Ping will deliver a null message.
            }
            throw new RuntimeException("No more messages expected, but received: " + m);
        }
    }
}
+61 −17
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.content.componentalias.tests.common.ComponentAliasTestComm
import static android.content.componentalias.tests.common.ComponentAliasTestCommon.SUB1_PACKAGE;
import static android.content.componentalias.tests.common.ComponentAliasTestCommon.SUB2_PACKAGE;
import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TAG;
import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TEST_PACKAGE;

import static com.google.common.truth.Truth.assertThat;

@@ -34,18 +33,16 @@ import android.os.IBinder;
import android.provider.DeviceConfig;
import android.util.Log;

import androidx.test.InstrumentationRegistry;

import com.android.compatibility.common.util.BroadcastMessenger;
import com.android.compatibility.common.util.BroadcastMessenger.Receiver;
import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.compatibility.common.util.ShellUtils;
import com.android.compatibility.common.util.TestUtils;

import androidx.test.InstrumentationRegistry;

import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

/**
@@ -95,7 +92,7 @@ public class ComponentAliasServiceTest {
                    .setMethodName("onServiceConnected")
                    .setComponent(name);

            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
            BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
        }

        @Override
@@ -107,7 +104,7 @@ public class ComponentAliasServiceTest {
                    .setMethodName("onServiceDisconnected")
                    .setComponent(name);

            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
            BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
        }

        @Override
@@ -118,7 +115,7 @@ public class ComponentAliasServiceTest {
                    .setSenderIdentity("sServiceConnection")
                    .setMethodName("onBindingDied");

            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
            BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
        }

        @Override
@@ -129,7 +126,7 @@ public class ComponentAliasServiceTest {
                    .setSenderIdentity("sServiceConnection")
                    .setMethodName("onNullBinding");

            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
            BroadcastMessenger.send(sContext, sContext.getPackageName(), m);
        }
    };

@@ -165,6 +162,8 @@ public class ComponentAliasServiceTest {
            m = receiver.waitForNextMessage();

            assertThat(m.getMethodName()).isEqualTo("onDestroy");

            receiver.ensureNoMoreMessages();
        }
    }

@@ -193,24 +192,29 @@ public class ComponentAliasServiceTest {
    @Test
    public void testStartAndStopService_override() throws Exception {
        Intent i = new Intent().setPackage(APP_PACKAGE);
        i.setAction(APP_PACKAGE + ".IS_ALIAS_02");
        i.setAction(APP_PACKAGE + ".IS_ALIAS_01");

        ComponentName alias = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
        // Change some of the aliases from what's defined in <meta-data>.

        ComponentName aliasA = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01");
        ComponentName targetA = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Target02");

        // Note, alias02 originally points at sub*2* package, but we override it in this test.
        ComponentName target = new ComponentName(SUB1_PACKAGE, APP_PACKAGE + ".s.Target02");
        ComponentName aliasB = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
        ComponentName targetB = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Target01");

        sDeviceConfig.set("component_alias_overrides",
                alias.flattenToShortString() + ":" + target.flattenToShortString());
                aliasA.flattenToShortString() + ":" + targetA.flattenToShortString()
                + ","
                + aliasB.flattenToShortString() + ":" + targetB.flattenToShortString());

        TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
            return ShellUtils.runShellCommand("dumpsys activity component-alias")
                    .indexOf(alias.flattenToShortString() + " -> " + target.flattenToShortString())
                    > 0;
                    .indexOf(aliasA.flattenToShortString()
                            + " -> " + targetA.flattenToShortString()) > 0;
        });


        testStartAndStopService_common(i, alias, target);
        testStartAndStopService_common(i, aliasA, targetA);
    }

    private void testBindAndUnbindService_common(
@@ -253,6 +257,7 @@ public class ComponentAliasServiceTest {
            assertThat(m.getMethodName()).isEqualTo("onDestroy");

            // Note onServiceDisconnected() won't be called in this case.
            receiver.ensureNoMoreMessages();
        }
    }

@@ -275,4 +280,43 @@ public class ComponentAliasServiceTest {
                new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02"),
                new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02"));
    }

    @Test
    public void testBindService_serviceKilled() throws Exception {
        Intent originalIntent = new Intent().setPackage(APP_PACKAGE);
        originalIntent.setAction(APP_PACKAGE + ".IS_ALIAS_02");

        final ComponentName componentNameForClient =
                new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
        final ComponentName componentNameForTarget =
                new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02");

        ComponentAliasMessage m;

        try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext)) {
            // Bind to the service.
            assertThat(sContext.bindService(
                    originalIntent, sServiceConnection, BIND_AUTO_CREATE)).isTrue();

            // Check the target side behavior.
            m = receiver.waitForNextMessage();

            assertThat(m.getMethodName()).isEqualTo("onBind");

            m = receiver.waitForNextMessage();
            assertThat(m.getMethodName()).isEqualTo("onServiceConnected");
            assertThat(m.getComponent()).isEqualTo(componentNameForClient);

            // Now kill the service process.
            ShellUtils.runShellCommand("su 0 killall %s", SUB2_PACKAGE);

            // Check the target side behavior.
            m = receiver.waitForNextMessage();

            assertThat(m.getMethodName()).isEqualTo("onServiceDisconnected");
            assertThat(m.getComponent()).isEqualTo(componentNameForClient);

            receiver.ensureNoMoreMessages();
        }
    }
}