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

Commit a6334bc0 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

Hide Linux terminal if device doesn't meet minimum requirement

Bug: 366374130
Test: Manually, atest
Change-Id: Id0ef47099b3fab41175bfae3d0eaf61037ba82ff
parent 005ee976
Loading
Loading
Loading
Loading
+27 −3
Original line number Diff line number Diff line
@@ -18,7 +18,10 @@ package com.android.settings.development.linuxterminal;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.DataUnit;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -28,30 +31,45 @@ import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

import java.util.Objects;

/** Preference controller for Linux terminal option in developers option */
public class LinuxTerminalPreferenceController extends DeveloperOptionsPreferenceController
        implements PreferenceControllerMixin {
    @VisibleForTesting
    static final int TERMINAL_PACKAGE_NAME_RESID = R.string.config_linux_terminal_app_package_name;

    @VisibleForTesting
    static final long MEMORY_MIN_BYTES = DataUnit.GIGABYTES.toBytes(4); // 4_000_000_000

    @VisibleForTesting
    static final long STORAGE_MIN_BYTES = DataUnit.GIBIBYTES.toBytes(128); // 128 * 2^30

    private static final String LINUX_TERMINAL_KEY = "linux_terminal";

    @Nullable private final String mTerminalPackageName;
    private final boolean mIsDeviceCapable;

    public LinuxTerminalPreferenceController(@NonNull Context context) {
        super(context);
        String packageName = context.getString(TERMINAL_PACKAGE_NAME_RESID);
        mTerminalPackageName =
                isPackageInstalled(context.getPackageManager(), packageName) ? packageName : null;

        StorageManager storageManager =
                Objects.requireNonNull(context.getSystemService(StorageManager.class));
        mIsDeviceCapable =
                getTotalMemory() >= MEMORY_MIN_BYTES
                        && storageManager.getPrimaryStorageSize() >= STORAGE_MIN_BYTES;
    }

    // Avoid lazy initialization because this may be called before displayPreference().
    @Override
    public boolean isAvailable() {
        // Returns true only if the terminal app is installed which only happens when the build flag
        // RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES is true.
        // Check build flag RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES indirectly
        // by checking whether the terminal app is installed.
        // TODO(b/343795511): Add explicitly check for the flag when it's accessible from Java code.
        return mTerminalPackageName != null;
        return mTerminalPackageName != null && mIsDeviceCapable;
    }

    @Override
@@ -73,4 +91,10 @@ public class LinuxTerminalPreferenceController extends DeveloperOptionsPreferenc
            return false;
        }
    }

    // Can be overridden for test
    @VisibleForTesting
    long getTotalMemory() {
        return Process.getTotalMemory();
    }
}
+40 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.development.linuxterminal;

import static com.android.settings.development.linuxterminal.LinuxTerminalPreferenceController.MEMORY_MIN_BYTES;
import static com.android.settings.development.linuxterminal.LinuxTerminalPreferenceController.STORAGE_MIN_BYTES;
import static com.android.settings.development.linuxterminal.LinuxTerminalPreferenceController.TERMINAL_PACKAGE_NAME_RESID;

import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +31,7 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.storage.StorageManager;

import org.junit.Before;
import org.junit.Test;
@@ -43,6 +46,7 @@ public class LinuxTerminalPreferenceControllerTest {

    @Mock private Context mContext;
    @Mock private PackageManager mPackageManager;
    @Mock private StorageManager mStorageManager;
    @Mock private PackageInfo mPackageInfo;

    private String mTerminalPackageName = "com.android.virtualization.terminal";
@@ -57,11 +61,30 @@ public class LinuxTerminalPreferenceControllerTest {
        doReturn(mPackageInfo)
                .when(mPackageManager)
                .getPackageInfo(eq(mTerminalPackageName), anyInt());

        doReturn(mStorageManager).when(mContext).getSystemService(StorageManager.class);
        doReturn(STORAGE_MIN_BYTES).when(mStorageManager).getPrimaryStorageSize();
    }

    @Test
    public void isAvailable_whenPackageExists_returnsTrue() throws NameNotFoundException {
        mController = new LinuxTerminalPreferenceController(mContext);
    public void isAvailable_whenMemoryInsufficient_returnFalse() {
        mController = createController(mContext, MEMORY_MIN_BYTES / 2);

        assertThat(mController.isAvailable()).isFalse();
    }

    @Test
    public void isAvailable_whenDeviceStorageInsufficient_returnFalse() {
        doReturn(STORAGE_MIN_BYTES / 2).when(mStorageManager).getPrimaryStorageSize();

        mController = createController(mContext);

        assertThat(mController.isAvailable()).isFalse();
    }

    @Test
    public void isAvailable_whenPackageExists_returnsTrue() {
        mController = createController(mContext);

        assertThat(mController.isAvailable()).isTrue();
    }
@@ -70,7 +93,7 @@ public class LinuxTerminalPreferenceControllerTest {
    public void isAvailable_whenPackageNameIsNull_returnsFalse() {
        doReturn(null).when(mContext).getString(TERMINAL_PACKAGE_NAME_RESID);

        mController = new LinuxTerminalPreferenceController(mContext);
        mController = createController(mContext);

        assertThat(mController.isAvailable()).isFalse();
    }
@@ -81,8 +104,21 @@ public class LinuxTerminalPreferenceControllerTest {
                .when(mPackageManager)
                .getPackageInfo(eq(mTerminalPackageName), anyInt());

        mController = new LinuxTerminalPreferenceController(mContext);
        mController = createController(mContext);

        assertThat(mController.isAvailable()).isFalse();
    }

    private LinuxTerminalPreferenceController createController(Context context) {
        return createController(context, MEMORY_MIN_BYTES);
    }

    private LinuxTerminalPreferenceController createController(Context context, long totalMemory) {
        return new LinuxTerminalPreferenceController(context) {
            @Override
            public long getTotalMemory() {
                return totalMemory;
            }
        };
    }
}