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

Commit 6b344aa4 authored by Jackal Guo's avatar Jackal Guo
Browse files

Add compatibility mode test

These test cases ensure system correctly handles the global state
of compatibility mode and related flags in the application info.

Bug: 186592266
Test: atest CompatibilityModeTest
Change-Id: Ia9cb6fe0404a53add2362c89d98e9363dec5aa2e
parent 8a520d60
Loading
Loading
Loading
Loading
+234 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.pm;

import static android.content.pm.ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.os.Build;

import com.android.server.pm.parsing.pkg.AndroidPackage;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class CompatibilityModeTest {

    private boolean mCompatibilityModeEnabled;;
    private AndroidPackage mMockAndroidPackage;
    private PackageUserState mMockUserState;

    @Before
    public void setUp() {
        mCompatibilityModeEnabled = PackageParser.sCompatibilityModeEnabled;
        mMockAndroidPackage = mock(AndroidPackage.class);
        mMockUserState = mock(PackageUserState.class);
        mMockUserState.installed = true;
        when(mMockUserState.isAvailable(anyInt())).thenReturn(true);
        when(mMockUserState.getAllOverlayPaths()).thenReturn(null);
    }

    @After
    public void tearDown() {
        setGlobalCompatibilityMode(mCompatibilityModeEnabled);
    }

    // The following tests ensure that apps with target SDK of Cupcake always use compat mode.

    @Test
    public void testGlobalCompatModeEnabled_oldApp_supportAllScreens_usesCompatMode() {
        setGlobalCompatibilityMode(true);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeEnabled_oldApp_supportSomeScreens_usesCompatMode() {
        setGlobalCompatibilityMode(true);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeEnabled_oldApp_supportOnlyOneScreen_usesCompatMode() {
        setGlobalCompatibilityMode(true);
        final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeEnabled_oldApp_DoesntSupportAllScreens_usesCompatMode() {
        setGlobalCompatibilityMode(true);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, 0 /*flags*/);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeDisabled_oldApp_supportAllScreens_usesCompatMode() {
        setGlobalCompatibilityMode(false);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeDisabled_oldApp_supportSomeScreens_usesCompatMode() {
        setGlobalCompatibilityMode(false);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeDisabled_oldApp_supportOnlyOneScreen_usesCompatMode() {
        setGlobalCompatibilityMode(false);
        final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, flags);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeDisabled_oldApp_doesntSupportAllScreens_usesCompatMode() {
        setGlobalCompatibilityMode(false);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.CUPCAKE, 0 /*flags*/);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    // The following tests ensure that apps with newer target SDK use compat mode as expected.

    @Test
    public void testGlobalCompatModeEnabled_newApp_supportAllScreens_doesntUseCompatMode() {
        setGlobalCompatibilityMode(true);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    @Test
    public void testGlobalCompatModeEnabled_newApp_supportSomeScreens_doesntUseCompatMode() {
        setGlobalCompatibilityMode(true);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    @Test
    public void testGlobalCompatModeEnabled_newApp_supportOnlyOneScreen_doesntUseCompatMode() {
        setGlobalCompatibilityMode(true);
        final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
        final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    @Test
    public void testGlobalCompatModeEnabled_newApp_doesntSupportAllScreens_usesCompatMode() {
        setGlobalCompatibilityMode(true);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.DONUT, 0 /*flags*/);
        assertThat(info.usesCompatibilityMode(), is(true));
    }

    @Test
    public void testGlobalCompatModeDisabled_newApp_supportAllScreens_doesntUseCompatMode() {
        setGlobalCompatibilityMode(false);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS | FLAG_SUPPORTS_NORMAL_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    @Test
    public void testGlobalCompatModeDisabled_newApp_supportSomeScreens_doesntUseCompatMode() {
        setGlobalCompatibilityMode(false);
        final int flags = (FLAG_SUPPORTS_LARGE_SCREENS
                | FLAG_SUPPORTS_SMALL_SCREENS | FLAG_RESIZEABLE_FOR_SCREENS
                | FLAG_SUPPORTS_SCREEN_DENSITIES | FLAG_SUPPORTS_XLARGE_SCREENS);
        final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    @Test
    public void testGlobalCompatModeDisabled_newApp_supportOnlyOneScreen_doesntUseCompatMode() {
        setGlobalCompatibilityMode(false);
        final int flags = FLAG_SUPPORTS_NORMAL_SCREENS;
        final ApplicationInfo info = generateMockApplicationInfo(Build.VERSION_CODES.DONUT, flags);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    @Test
    public void testGlobalCompatModeDisabled_newApp_doesntSupportAllScreens_doesntUseCompatMode() {
        setGlobalCompatibilityMode(false);
        final ApplicationInfo info =
                generateMockApplicationInfo(Build.VERSION_CODES.DONUT, 0 /*flags*/);
        assertThat(info.usesCompatibilityMode(), is(false));
    }

    private ApplicationInfo generateMockApplicationInfo(int targetSdkVersion, int flags) {
        final ApplicationInfo info = new ApplicationInfo();
        info.targetSdkVersion = targetSdkVersion;
        info.flags |= flags;
        when(mMockAndroidPackage.toAppInfoWithoutState()).thenReturn(info);
        return PackageInfoWithoutStateUtils.generateApplicationInfoUnchecked(mMockAndroidPackage,
                0 /*flags*/, mMockUserState, 0 /*userId*/, false /*assignUserFields*/);
    }

    private void setGlobalCompatibilityMode(boolean enabled) {
        if (PackageParser.sCompatibilityModeEnabled == enabled) {
            return;
        }
        PackageParser.setCompatibilityModeEnabled(enabled);
    }
}