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

Commit 16d42932 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Enable FGS BG start restriction for apps targeting S

Also temporarily exempt Chrome, WebView and their variants, which
already all target S.

Bug: 175310205
Test: Boot, presublit
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
Change-Id: I5b7aeb3d2b37f410e8b7220d3c3996e2296437c8
parent 583b2e50
Loading
Loading
Loading
Loading
+55 −11
Original line number Diff line number Diff line
@@ -61,7 +61,6 @@ import android.app.admin.DevicePolicyEventLogger;
import android.app.compat.CompatChanges;
import android.appwidget.AppWidgetManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
@@ -108,6 +107,7 @@ import android.webkit.WebViewZygote;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ServiceState;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
@@ -273,7 +273,7 @@ public final class ActiveServices {
     * is higher than R.
     */
    @ChangeId
    @Disabled
    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
    static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;

    /**
@@ -305,9 +305,40 @@ public final class ActiveServices {
     */
    private static final ArraySet<String> sFgsBgStartExemptedPackages = new ArraySet<>();

    private static final ArrayList<String> sFgsBgStartExemptedPackagePrefixes = new ArrayList<>();

    /**
     * List of packages that are exempted from the FGS restriction *for now*.
     *
     * STOPSHIP(/b/176844961) Remove it. Also update ActiveServicesTest.java.
     */
    private static final String[] FGS_BG_START_EXEMPTED_PACKAGES = {
            "com.google.pixel.exo.bootstrapping",
    };

    /**
     * List of packages that are exempted from the FGS restriction *for now*. We also allow
     * any packages that
     *
     * STOPSHIP(/b/176844961) Remove it. Also update ActiveServicesTest.java.
     */
    private static final String[] FGS_BG_START_EXEMPTED_PACKAGES_PREFIXED_ALLOWED = {
            "com.android.webview",
            "com.google.android.webview",
            "com.android.chrome",
            "com.google.android.apps.chrome",
            "com.chrome",
    };

    static {
        sFgsBgStartExemptedPackages.add("com.google.pixel.exo.bootstrapping"); //STOPSHIP Remove it.
        sFgsBgStartExemptedPackages.add("com.android.chrome"); // STOPSHIP Remove it.
        for (String s : FGS_BG_START_EXEMPTED_PACKAGES) {
            sFgsBgStartExemptedPackages.add(s);
        }

        for (String s : FGS_BG_START_EXEMPTED_PACKAGES_PREFIXED_ALLOWED) {
            sFgsBgStartExemptedPackages.add(s); // Add it for an exact match.
            sFgsBgStartExemptedPackagePrefixes.add(s + "."); // Add it for an prefix match.
        }
    }

    final Runnable mLastAnrDumpClearer = new Runnable() {
@@ -5357,10 +5388,25 @@ public final class ActiveServices {
        return ret;
    }

    private boolean isPackageExemptedFromFgsRestriction(String packageName, int uid) {
        if (!sFgsBgStartExemptedPackages.contains(packageName)) {
            return false;
    @VisibleForTesting
    static boolean isPackageExemptedFromFgsRestriction(String packageName, int uid) {
        boolean exempted = false;
        if (sFgsBgStartExemptedPackages.contains(packageName)) {
            exempted = true;
        } else {
            for (String pkg : sFgsBgStartExemptedPackagePrefixes) {
                if (packageName.startsWith(pkg)) {
                    exempted = true;
                    break;
                }
            }
        }
        if (!exempted) {
            return false; // Package isn't exempted.
        }
        // Allow exempted packages to be subject to the restriction using this compat ID.
        // (so that, for example, the webview developer will be able to test the restriction
        // locally.)
        return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid);
    }

@@ -5441,9 +5487,7 @@ public final class ActiveServices {
    }

    private boolean isBgFgsRestrictionEnabled(ServiceRecord r) {
        if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
            return true;
        }
        return CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
        return mAm.mConstants.mFlagFgsStartRestrictionEnabled
                && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
    }
}
+4 −3
Original line number Diff line number Diff line
@@ -360,10 +360,11 @@ final class ActivityManagerConstants extends ContentObserver {
    // started, the restriction is on while-in-use permissions.)
    volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;

    // Indicates whether the foreground service background start restriction is enabled.
    // Indicates whether the foreground service background start restriction is enabled for
    // apps targeting S+.
    // When the restriction is enabled, service is not allowed to startForeground from background
    // at all.
    volatile boolean mFlagFgsStartRestrictionEnabled = false;
    volatile boolean mFlagFgsStartRestrictionEnabled = true;

    // Whether we defer FGS notifications a few seconds following their transition to
    // the foreground state.  Applies only to S+ apps; enabled by default.
@@ -792,7 +793,7 @@ final class ActivityManagerConstants extends ContentObserver {
        mFlagFgsStartRestrictionEnabled = DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED,
                /*defaultValue*/ false);
                /*defaultValue*/ true);
    }

    private void updateFgsNotificationDeferralEnable() {
+95 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.am;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.am.ActiveServices.FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;

import android.app.compat.CompatChanges;

import androidx.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;



@RunWith(AndroidJUnit4.class)
public class ActiveServicesTest {

    private MockitoSession mMockingSession;

    @Before
    public void setUp() {
        mMockingSession = mockitoSession()
                .initMocks(this)
                .strictness(Strictness.LENIENT)
                .mockStatic(CompatChanges.class)
                .startMocking();
    }

    @After
    public void tearDown() {
        if (mMockingSession != null) {
            mMockingSession.finishMocking();
        }
    }

    private void checkPackageExempted(String pkg, int uid, boolean expected) {
        assertEquals("Package=" + pkg + " uid=" + uid,
                expected, ActiveServices.isPackageExemptedFromFgsRestriction(pkg, uid));
    }

    @Test
    public void isPackageExemptedFromFgsRestriction() {
        // Compat changes are enabled by default.
        when(CompatChanges.isChangeEnabled(anyLong(), anyInt())).thenReturn(true);

        checkPackageExempted("", 1, false);
        checkPackageExempted("abc", 1, false);
        checkPackageExempted("com.random", 1, false);

        // This package is exempted but not its subpackages.
        checkPackageExempted("com.google.pixel.exo.bootstrapping", 1, true);
        checkPackageExempted("com.google.pixel.exo.bootstrapping.subpackage", 1, false);

        // Subpackages are also exempted.
        checkPackageExempted("com.android.webview", 1, true);
        checkPackageExempted("com.android.webview.beta", 1, true);
        checkPackageExempted("com.chrome", 1, true);
        checkPackageExempted("com.chrome.canary", 1, true);

        checkPackageExempted("com.android.webviewx", 1, false);

        // Now toggle the compat ID for a specific UID.
        when(CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, 10))
                .thenReturn(false);
        // Exempted package, but compat id is disabled for the UID.
        checkPackageExempted("com.android.webview", 10, false);

        // Exempted package, but compat id is still enabled for the UID.
        checkPackageExempted("com.android.webview", 11, true);
    }
}