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

Commit ab78ebe1 authored by Michael Bestas's avatar Michael Bestas
Browse files

Merge tag 'android-security-14.0.0_r16' of...

Merge tag 'android-security-14.0.0_r16' of https://android.googlesource.com/platform/frameworks/base into lineage-21.0

Android security 14.0.0 release 16

* tag 'android-security-14.0.0_r16' of https://android.googlesource.com/platform/frameworks/base:
  Always show all approved apps
  Pass SafeActivityOptions with actual caller for startActivityInTF
  Update checkKeyIntent
  Fix allowlist token issues
  Enforce hard limits on hosts per package and widgets per host.
  enforce limits for VisualVoicemailSmsFilterSettings properties

 Conflicts:
	core/java/android/app/Notification.java
	core/tests/coretests/src/android/os/ParcelTest.java
	services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java

Change-Id: I9530fca5ed26da32a1eb5f5ee9860992ae0957e4
parents 3cb1e421 0a23febf
Loading
Loading
Loading
Loading
+35 −6
Original line number Diff line number Diff line
@@ -2641,8 +2641,11 @@ public class Notification implements Parcelable
        if (mAllowlistToken == null) {
            mAllowlistToken = processAllowlistToken;
        }
        // Propagate this token to all pending intents that are unmarshalled from the parcel.
        // Propagate this token to all pending intents that are unmarshalled from the parcel,
        // or keep the one we're already propagating, if that's the case.
        if (!parcel.hasClassCookie(PendingIntent.class)) {
            parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
        }
        when = parcel.readLong();
        creationTime = parcel.readLong();
@@ -3175,10 +3178,25 @@ public class Notification implements Parcelable
            };
            PendingIntent.addOnMarshaledListener(addedListener);
        }
        try {
            boolean mustClearCookie = false;
            if (!parcel.hasClassCookie(Notification.class)) {
                // This is the "root" notification, and not an "inner" notification (including
                // publicVersion or anything else that might be embedded in extras). So we want
                // to use its token for every inner notification (might be null).
                parcel.setClassCookie(Notification.class, mAllowlistToken);
                mustClearCookie = true;
            }
            try {
                // IMPORTANT: Add marshaling code in writeToParcelImpl as we
                // want to intercept all pending events written to the parcel.
                writeToParcelImpl(parcel, flags);
            } finally {
                if (mustClearCookie) {
                    parcel.removeClassCookie(Notification.class, mAllowlistToken);
                }
            }
            synchronized (this) {
                // Must be written last!
                parcel.writeArraySet(allPendingIntents);
@@ -3193,7 +3211,10 @@ public class Notification implements Parcelable
    private void writeToParcelImpl(Parcel parcel, int flags) {
        parcel.writeInt(1);
        parcel.writeStrongBinder(mAllowlistToken);
        // Always use the same token as the root notification (might be null).
        IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
        parcel.writeStrongBinder(rootNotificationToken);
        parcel.writeLong(when);
        parcel.writeLong(creationTime);
        if (mSmallIcon == null && icon != 0) {
@@ -3586,6 +3607,9 @@ public class Notification implements Parcelable
     * Sets the token used for background operations for the pending intents associated with this
     * notification.
     *
     * Note: Should <em>only</em> be invoked by NotificationManagerService, since this is normally
     * populated by unparceling (and also used there). Any other usage is suspect.
     *
     * @hide
     */
    public void overrideAllowlistToken(IBinder token) {
@@ -3595,6 +3619,11 @@ public class Notification implements Parcelable
        }
    }
    /** @hide */
    public IBinder getAllowlistToken() {
        return mAllowlistToken;
    }
    /**
     * @hide
     */
+22 −0
Original line number Diff line number Diff line
@@ -853,6 +853,28 @@ public final class Parcel {
        return mClassCookies != null ? mClassCookies.get(clz) : null;
    }

    /** @hide */
    public void removeClassCookie(Class clz, Object expectedCookie) {
        if (mClassCookies != null) {
            Object removedCookie = mClassCookies.remove(clz);
            if (removedCookie != expectedCookie) {
                Log.wtf(TAG, "Expected to remove " + expectedCookie + " (with key=" + clz
                        + ") but instead removed " + removedCookie);
            }
        } else {
            Log.wtf(TAG, "Expected to remove " + expectedCookie + " (with key=" + clz
                    + ") but no cookies were present");
        }
    }

    /**
     * Whether {@link #setClassCookie} has been called with the specified {@code clz}.
     * @hide
     */
    public boolean hasClassCookie(Class clz) {
        return mClassCookies != null && mClassCookies.containsKey(clz);
    }

    /** @hide */
    public final void adoptClassCookies(Parcel from) {
        mClassCookies = from.mClassCookies;
+49 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.os;

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

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
@@ -24,6 +26,7 @@ import static org.junit.Assert.assertTrue;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.annotations.Presubmit;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;

import androidx.test.runner.AndroidJUnit4;

@@ -31,6 +34,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;

@Presubmit
@RunWith(AndroidJUnit4.class)
public class ParcelTest {
@@ -347,4 +352,48 @@ public class ParcelTest {
        p.recycle();
        Binder.setIsDirectlyHandlingTransactionOverride(false);
    }

    @Test
    public void testClassCookies() {
        Parcel p = Parcel.obtain();
        assertThat(p.hasClassCookie(ParcelTest.class)).isFalse();

        p.setClassCookie(ParcelTest.class, "string_cookie");
        assertThat(p.hasClassCookie(ParcelTest.class)).isTrue();
        assertThat(p.getClassCookie(ParcelTest.class)).isEqualTo("string_cookie");

        p.removeClassCookie(ParcelTest.class, "string_cookie");
        assertThat(p.hasClassCookie(ParcelTest.class)).isFalse();
        assertThat(p.getClassCookie(ParcelTest.class)).isEqualTo(null);

        p.setClassCookie(ParcelTest.class, "to_be_discarded_cookie");
        p.recycle();
        assertThat(p.getClassCookie(ParcelTest.class)).isNull();
    }

    @Test
    public void testClassCookies_removeUnexpected() {
        Parcel p = Parcel.obtain();

        assertLogsWtf(() -> p.removeClassCookie(ParcelTest.class, "not_present"));

        p.setClassCookie(ParcelTest.class, "value");

        assertLogsWtf(() -> p.removeClassCookie(ParcelTest.class, "different"));
        assertThat(p.getClassCookie(ParcelTest.class)).isNull(); // still removed

        p.recycle();
    }

    private static void assertLogsWtf(Runnable test) {
        ArrayList<Log.TerribleFailure> wtfs = new ArrayList<>();
        Log.TerribleFailureHandler oldHandler = Log.setWtfHandler(
                (tag, what, system) -> wtfs.add(what));
        try {
            test.run();
        } finally {
            Log.setWtfHandler(oldHandler);
        }
        assertThat(wtfs).hasSize(1);
    }
}
+23 −9
Original line number Diff line number Diff line
@@ -138,11 +138,13 @@ public class ServiceListing {
        }

        final PackageManager pmWrapper = mContext.getPackageManager();
        // Add requesting apps, with full validation
        List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser(
                new Intent(mIntentAction), flags, user);
        for (ResolveInfo resolveInfo : installedServices) {
            ServiceInfo info = resolveInfo.serviceInfo;

            if (!mEnabledServices.contains(info.getComponentName())) {
                if (!mPermission.equals(info.permission)) {
                    Slog.w(mTag, "Skipping " + mNoun + " service "
                            + info.packageName + "/" + info.name
@@ -155,6 +157,18 @@ public class ServiceListing {
                }
                mServices.add(info);
            }
        }

        // Add all apps with access, in case prior approval was granted without full validation
        for (ComponentName cn : mEnabledServices) {
            List<ResolveInfo> enabledServices = pmWrapper.queryIntentServicesAsUser(
                    new Intent().setComponent(cn), flags, user);
            for (ResolveInfo resolveInfo : enabledServices) {
                ServiceInfo info = resolveInfo.serviceInfo;
                mServices.add(info);
            }
        }

        for (Callback callback : mCallbacks) {
            callback.onServicesReloaded(mServices);
        }
+65 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -29,6 +30,7 @@ import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -42,6 +44,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

@@ -72,19 +75,26 @@ public class ServiceListingTest {
                .build();
    }

    private ArgumentMatcher<Intent> filterEquals(Intent intent) {
        return (test) -> {
            return intent.filterEquals(test);
        };
    }

    @Test
    public void testValidator() {
        ServiceInfo s1 = new ServiceInfo();
        s1.permission = "testPermission";
        s1.packageName = "pkg";
        s1.name = "Service1";
        ServiceInfo s2 = new ServiceInfo();
        s2.permission = "testPermission";
        s2.packageName = "pkg2";
        s2.name = "service2";
        ResolveInfo r1 = new ResolveInfo();
        r1.serviceInfo = s1;
        ResolveInfo r2 = new ResolveInfo();
        r2.serviceInfo = s2;

        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(
                ImmutableList.of(r1, r2));

@@ -118,9 +128,11 @@ public class ServiceListingTest {
        ServiceInfo s1 = new ServiceInfo();
        s1.permission = "testPermission";
        s1.packageName = "pkg";
        s1.name = "Service1";
        ServiceInfo s2 = new ServiceInfo();
        s2.permission = "testPermission";
        s2.packageName = "pkg2";
        s2.name = "service2";
        ResolveInfo r1 = new ResolveInfo();
        r1.serviceInfo = s1;
        ResolveInfo r2 = new ResolveInfo();
@@ -193,4 +205,56 @@ public class ServiceListingTest {
        assertThat(Settings.Secure.getString(RuntimeEnvironment.application.getContentResolver(),
                TEST_SETTING)).contains(testComponent2.flattenToString());
    }

    @Test
    public void testHasPermissionWithoutMeetingCurrentRegs() {
        ServiceInfo s1 = new ServiceInfo();
        s1.permission = "testPermission";
        s1.packageName = "pkg";
        s1.name = "Service1";
        ServiceInfo s2 = new ServiceInfo();
        s2.permission = "testPermission";
        s2.packageName = "pkg2";
        s2.name = "service2";
        ResolveInfo r1 = new ResolveInfo();
        r1.serviceInfo = s1;
        ResolveInfo r2 = new ResolveInfo();
        r2.serviceInfo = s2;

        ComponentName approvedComponent = new ComponentName(s2.packageName, s2.name);

        Settings.Secure.putString(
                mContext.getContentResolver(), TEST_SETTING, approvedComponent.flattenToString());

        when(mPm.queryIntentServicesAsUser(argThat(
                filterEquals(new Intent(TEST_INTENT))), anyInt(), anyInt()))
                .thenReturn(ImmutableList.of(r1));
        when(mPm.queryIntentServicesAsUser(argThat(
                filterEquals(new Intent().setComponent(approvedComponent))),
                anyInt(), anyInt()))
                .thenReturn(ImmutableList.of(r2));

        mServiceListing = new ServiceListing.Builder(mContext)
                .setTag("testTag")
                .setSetting(TEST_SETTING)
                .setNoun("testNoun")
                .setIntentAction(TEST_INTENT)
                .setValidator(info -> {
                    if (info.packageName.equals("pkg")) {
                        return true;
                    }
                    return false;
                })
                .setPermission("testPermission")
                .build();
        ServiceListing.Callback callback = mock(ServiceListing.Callback.class);
        mServiceListing.addCallback(callback);
        mServiceListing.reload();

        verify(mPm, times(2)).queryIntentServicesAsUser(any(), anyInt(), anyInt());
        ArgumentCaptor<List<ServiceInfo>> captor = ArgumentCaptor.forClass(List.class);
        verify(callback, times(1)).onServicesReloaded(captor.capture());

        assertThat(captor.getValue()).containsExactlyElementsIn(ImmutableList.of(s2, s1));
    }
}
Loading