Files
app_Settings/src/com/android/settings/development/linuxterminal/LinuxTerminalPreferenceController.java
Alex Stetson 99f01f7e7a Allow system services to be optional
Some Android systems may not have certain system services (particularly the VirtualMachineManager). If this is the case, disable the preference rather than requiring the service and crashing the Settings app.

Bug: 388221800
Test: manual
Flag: NONE bugfix
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5a594828d7e148ac98b077591e8050e424b54966)
Merged-In: I761054caebe3e846f5c6c76e2818662aad2cb511
Change-Id: I761054caebe3e846f5c6c76e2818662aad2cb511
2025-01-24 00:22:14 -08:00

108 lines
4.1 KiB
Java

/*
* Copyright 2024 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.settings.development.linuxterminal;
import static android.system.virtualmachine.VirtualMachineManager.CAPABILITY_NON_PROTECTED_VM;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.storage.StorageManager;
import android.system.virtualmachine.VirtualMachineManager;
import android.text.TextUtils;
import android.util.DataUnit;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
/** 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.GIGABYTES.toBytes(64); // 64_000_000_000
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 = context.getSystemService(StorageManager.class);
VirtualMachineManager virtualMachineManager =
context.getSystemService(VirtualMachineManager.class);
mIsDeviceCapable =
getTotalMemory() >= MEMORY_MIN_BYTES
&& storageManager != null
&& storageManager.getPrimaryStorageSize() >= STORAGE_MIN_BYTES
&& virtualMachineManager != null
&& ((virtualMachineManager.getCapabilities() & CAPABILITY_NON_PROTECTED_VM)
!= 0);
}
// Avoid lazy initialization because this may be called before displayPreference().
@Override
public boolean isAvailable() {
// 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 && mIsDeviceCapable;
}
@Override
@NonNull
public String getPreferenceKey() {
return LINUX_TERMINAL_KEY;
}
private static boolean isPackageInstalled(PackageManager manager, String packageName) {
if (TextUtils.isEmpty(packageName)) {
return false;
}
try {
return manager.getPackageInfo(
packageName,
PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS)
!= null;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
// Can be overridden for test
@VisibleForTesting
long getTotalMemory() {
return Process.getTotalMemory();
}
}