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

Commit f581764a authored by Achim Thesmann's avatar Achim Thesmann
Browse files

Unit Tests for BackgroundActivityStartController

Introduce a class that does the necessary setup to test
BackgroundActivityStartController in isolation, with 3 tests to demo it.

Test: atest BackgroundActivityStartControllerTests
Bug: TBD
Change-Id: I0dede1ebab4ebd871f197dde8f00344131dbf89c
parent 1927527c
Loading
Loading
Loading
Loading
+5 −4
Original line number Original line Diff line number Diff line
@@ -67,6 +67,7 @@ import android.util.Slog;
import android.widget.Toast;
import android.widget.Toast;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import com.android.server.UiThread;
@@ -221,7 +222,7 @@ public class BackgroundActivityStartController {
        return activity != null && packageName.equals(activity.getPackageName());
        return activity != null && packageName.equals(activity.getPackageName());
    }
    }


    private class BalState {
    @VisibleForTesting class BalState {


        private final String mCallingPackage;
        private final String mCallingPackage;
        private final int mCallingUid;
        private final int mCallingUid;
@@ -381,7 +382,7 @@ public class BackgroundActivityStartController {
            if (uid == 0) {
            if (uid == 0) {
                return "root[debugOnly]";
                return "root[debugOnly]";
            }
            }
            String name = mService.mContext.getPackageManager().getNameForUid(uid);
            String name = mService.getPackageManagerInternalLocked().getNameForUid(uid);
            if (name == null) {
            if (name == null) {
                name = "uid=" + uid;
                name = "uid=" + uid;
            }
            }
@@ -1195,7 +1196,7 @@ public class BackgroundActivityStartController {
        }
        }
    }
    }


    private void showToast(String toastText) {
    @VisibleForTesting void showToast(String toastText) {
        UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
        UiThread.getHandler().post(() -> Toast.makeText(mService.mContext,
                toastText, Toast.LENGTH_LONG).show());
                toastText, Toast.LENGTH_LONG).show());
    }
    }
@@ -1609,7 +1610,7 @@ public class BackgroundActivityStartController {
        return finalVerdict;
        return finalVerdict;
    }
    }


    private static void writeBalAllowedLog(String activityName, int code, BalState state) {
    @VisibleForTesting void writeBalAllowedLog(String activityName, int code, BalState state) {
        FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
        FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
                activityName,
                activityName,
                code,
                code,
+1 −0
Original line number Original line Diff line number Diff line
@@ -413,6 +413,7 @@ public class ActivityStarterTests extends WindowTestsBase {
                });
                });
        doReturn(null).when(mMockPackageManager).getDefaultHomeActivity(anyInt());
        doReturn(null).when(mMockPackageManager).getDefaultHomeActivity(anyInt());
        doReturn(mMockPackageManager).when(mAtm).getPackageManagerInternalLocked();
        doReturn(mMockPackageManager).when(mAtm).getPackageManagerInternalLocked();
        doReturn("packageName").when(mMockPackageManager).getNameForUid(anyInt());
        doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
        doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
        doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
        doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
                anyInt(), anyBoolean(), anyInt());
                anyInt(), anyBoolean(), anyInt());
+254 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 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.wm;

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

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;

import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;

import androidx.test.filters.SmallTest;

import com.android.compatibility.common.util.DeviceConfigStateHelper;
import com.android.server.am.PendingIntentRecord;
import com.android.server.wm.BackgroundActivityStartController.BalVerdict;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * Tests for the {@link ActivityStarter} class.
 *
 * Build/Install/Run:
 * atest WmTests:BackgroundActivityStartControllerTests
 */
@SmallTest
@Presubmit
@RunWith(JUnit4.class)
public class BackgroundActivityStartControllerTests {

    private static final int REGULAR_UID_1 = 10001;
    private static final int REGULAR_UID_2 = 10002;
    private static final int NO_UID = 01;
    private static final int REGULAR_PID_1 = 11001;
    private static final int REGULAR_PID_2 = 11002;
    private static final int NO_PID = 01;
    private static final String REGULAR_PACKAGE_1 = "package.app1";
    private static final String REGULAR_PACKAGE_2 = "package.app2";

    public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

    BackgroundActivityStartController mController;
    @Mock
    ActivityMetricsLogger mActivityMetricsLogger;
    @Mock
    WindowProcessController mCallerApp;
    DeviceConfigStateHelper mDeviceConfig = new DeviceConfigStateHelper(
            DeviceConfig.NAMESPACE_WINDOW_MANAGER);
    @Mock
    ActivityRecord mResultRecord;

    @Mock
    ActivityTaskManagerService mService;
    @Mock
    Context /* mService. */ mContext;
    @Mock
    PackageManagerInternal /* mService. */ mPackageManagerInternal;
    @Mock
    RootWindowContainer /* mService. */ mRootWindowContainer;
    @Mock
    AppOpsManager mAppOpsManager;
    MirrorActiveUids mActiveUids = new MirrorActiveUids();
    WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap();

    @Mock
    ActivityTaskSupervisor mSupervisor;
    @Mock
    RecentTasks /* mSupervisor. */ mRecentTasks;

    @Mock
    PendingIntentRecord mPendingIntentRecord; // just so we can pass a non-null instance

    record BalAllowedLog(String packageName, int code) {
    }

    List<String> mShownToasts = new ArrayList<>();
    List<BalAllowedLog> mBalAllowedLogs = new ArrayList<>();

    @Before
    public void setUp() throws Exception {
        // wire objects
        mService.mTaskSupervisor = mSupervisor;
        mService.mContext = mContext;
        setViaReflection(mService, "mActiveUids", mActiveUids);
        Mockito.when(mService.getPackageManagerInternalLocked()).thenReturn(
                mPackageManagerInternal);
        mService.mRootWindowContainer = mRootWindowContainer;
        Mockito.when(mService.getAppOpsManager()).thenReturn(mAppOpsManager);
        setViaReflection(mService, "mProcessMap", mProcessMap);

        //Mockito.when(mSupervisor.getBackgroundActivityLaunchController()).thenReturn(mController);
        setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks);

        mController = new BackgroundActivityStartController(mService, mSupervisor) {
            @Override
            protected void showToast(String toastText) {
                mShownToasts.add(toastText);
            }

            @Override
            protected void writeBalAllowedLog(String activityName, int code,
                    BackgroundActivityStartController.BalState state) {
                mBalAllowedLogs.add(new BalAllowedLog(activityName, code));
            }
        };

        // safe defaults
        Mockito.when(mAppOpsManager.checkOpNoThrow(
                eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
                anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
        Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
                BalVerdict.BLOCK);

    }

    private void setViaReflection(Object o, String property, Object value) {
        try {
            Field field = o.getClass().getDeclaredField(property);
            field.setAccessible(true);
            field.set(o, value);
        } catch (IllegalAccessException | NoSuchFieldException e) {
            throw new IllegalArgumentException("Cannot set " + property + " of " + o.getClass(), e);
        }
    }

    @After
    public void tearDown() throws Exception {
    }

    @Test
    public void testRegularActivityStart_noExemption_isBlocked() {
        // setup state

        // prepare call
        int callingUid = REGULAR_UID_1;
        int callingPid = REGULAR_PID_1;
        final String callingPackage = REGULAR_PACKAGE_1;
        int realCallingUid = NO_UID;
        int realCallingPid = NO_PID;
        PendingIntentRecord originatingPendingIntent = null;
        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
        Intent intent = new Intent();
        ActivityOptions checkedOptions = ActivityOptions.makeBasic();

        // call
        BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                callingPackage, realCallingUid, realCallingPid, mCallerApp,
                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
                checkedOptions);

        // assertions
        assertThat(verdict.getCode()).isEqualTo(BackgroundActivityStartController.BAL_BLOCK);

        assertThat(mBalAllowedLogs).isEmpty();
    }

    @Test
    public void testRegularActivityStart_allowedBLPC_isAllowed() {
        // setup state
        BalVerdict blpcVerdict = new BalVerdict(
                BackgroundActivityStartController.BAL_ALLOW_PERMISSION, true, "Allowed by BLPC");
        Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
                blpcVerdict);

        // prepare call
        int callingUid = REGULAR_UID_1;
        int callingPid = REGULAR_PID_1;
        final String callingPackage = REGULAR_PACKAGE_1;
        int realCallingUid = NO_UID;
        int realCallingPid = NO_PID;
        PendingIntentRecord originatingPendingIntent = null;
        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
        Intent intent = new Intent();
        ActivityOptions checkedOptions = ActivityOptions.makeBasic();

        // call
        BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                callingPackage, realCallingUid, realCallingPid, mCallerApp,
                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
                checkedOptions);

        // assertions
        assertThat(verdict).isEqualTo(blpcVerdict);
        assertThat(mBalAllowedLogs).containsExactly(
                new BalAllowedLog("", BackgroundActivityStartController.BAL_ALLOW_PERMISSION));
    }

    @Test
    public void testRegularActivityStart_allowedByCallerBLPC_isAllowed() {
        // setup state
        BalVerdict blpcVerdict = new BalVerdict(
                BackgroundActivityStartController.BAL_ALLOW_PERMISSION, true, "Allowed by BLPC");
        Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
                blpcVerdict);

        // prepare call
        int callingUid = REGULAR_UID_1;
        int callingPid = REGULAR_PID_1;
        final String callingPackage = REGULAR_PACKAGE_1;
        int realCallingUid = REGULAR_UID_2;
        int realCallingPid = REGULAR_PID_2;
        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
        Intent intent = new Intent();
        ActivityOptions checkedOptions = ActivityOptions.makeBasic();

        // call
        BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
                callingPackage, realCallingUid, realCallingPid, mCallerApp,
                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
                checkedOptions);

        // assertions
        assertThat(verdict).isEqualTo(blpcVerdict);
        assertThat(mBalAllowedLogs).containsExactly(
                new BalAllowedLog("", BackgroundActivityStartController.BAL_ALLOW_PERMISSION));
    }
}