Snap for 11811809 from 93fe16fc25 to 24Q3-release

Change-Id: I538b38b49b8f31ea11d61480b4c6777708f44eaa
This commit is contained in:
Android Build Coastguard Worker
2024-05-07 23:22:33 +00:00
50 changed files with 879 additions and 333 deletions

View File

@@ -240,6 +240,28 @@
</intent-filter>
</receiver>
<receiver
android:name=".development.Enable16KBootReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".development.PageAgnosticNotificationService"
android:enabled="true"
android:exported="false"
android:permission="android.permission.POST_NOTIFICATIONS"/>
<activity android:name=".development.PageAgnosticWarningActivity"
android:enabled="true"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true"
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"/>
<activity android:name=".SubSettings"
android:exported="false"
android:theme="@style/Theme.SubSettings"

View File

@@ -20,7 +20,7 @@ java_aconfig_library {
aconfig_declarations {
name: "factory_reset_flags",
package: "com.android.settings.factory_reset",
container: "system",
container: "system_ext",
srcs: ["factory_reset/*.aconfig"],
}
@@ -32,7 +32,7 @@ java_aconfig_library {
aconfig_declarations {
name: "media_drm_flags",
package: "com.android.settings.media_drm",
container: "system",
container: "system_ext",
srcs: ["media_drm/*.aconfig"],
}
@@ -44,7 +44,7 @@ java_aconfig_library {
aconfig_declarations {
name: "accessibility_flags",
package: "com.android.settings.accessibility",
container: "system",
container: "system_ext",
srcs: ["accessibility/*.aconfig"],
}
@@ -56,7 +56,7 @@ java_aconfig_library {
aconfig_declarations {
name: "development_settings_flags",
package: "com.android.settings.development",
container: "system",
container: "system_ext",
srcs: [
"development/**/*.aconfig",
],

View File

@@ -1,5 +1,5 @@
package: "com.android.settings.accessibility"
container: "system"
container: "system_ext"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.

View File

@@ -1,5 +1,5 @@
package: "com.android.settings.development"
container: "system"
container: "system_ext"
flag {
name: "a2dp_offload_codec_extensibility_settings"

View File

@@ -1,5 +1,5 @@
package: "com.android.settings.factory_reset"
container: "system"
container: "system_ext"
flag {
name: "enable_factory_reset_wizard"

View File

@@ -1,9 +1,9 @@
package: "com.android.settings.media_drm"
container: "system"
container: "system_ext"
flag {
name: "force_l3_enabled"
namespace: "media_drm"
description: "Feature flag of forcing L3"
bug: "301669353"
}
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/system_surface_dim_dark" />
</selector>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/system_surface_dim_light" />
</selector>

View File

@@ -14,5 +14,5 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?android:attr/colorAccent"/>
<item android:color="@color/settingslib_materialColorPrimary"/>
</selector>

View File

@@ -16,8 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid android:color="?androidprv:attr/materialColorPrimaryContainer" />
<solid android:color="@color/settingslib_materialColorPrimaryContainer" />
<corners android:radius="@dimen/battery_hints_chip_corner_radius" />
</shape>

View File

@@ -17,6 +17,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/settingslib_dialog_background" />
<solid android:color="@color/settingslib_materialColorSurfaceBright" />
<corners android:radius="@dimen/battery_tips_card_corner_radius_normal" />
</shape>

View File

@@ -25,7 +25,7 @@
android:layout_marginTop="8dp"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"
android:textColor="?android:attr/textColorPrimary" />
android:textColor="@color/settingslib_materialColorOnSurface" />
<LinearLayout
android:layout_width="match_parent"
@@ -55,7 +55,7 @@
android:paddingHorizontal="16dp"
android:text="@string/battery_tips_card_action_button"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
android:textColor="@color/power_anomaly_primary_button_text_color"
android:textColor="@color/settingslib_materialColorOnPrimary"
app:backgroundTint="@color/color_accent_selector" />
</LinearLayout>
</LinearLayout>

View File

@@ -38,6 +38,6 @@
android:paddingHorizontal="8dp"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle2"
android:textColor="?androidprv:attr/materialColorOnSurface"/>
android:textColor="@color/settingslib_materialColorOnSurface"/>
</LinearLayout>

View File

@@ -58,7 +58,6 @@
<!-- Power anomaly color for icons, button and text -->
<color name="power_anomaly_app_warning_hint_color">#FDD663</color>
<color name="power_anomaly_primary_button_text_color">#2E3300</color>
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#7DA7F1</color>

View File

@@ -20,7 +20,7 @@
<style name="Theme.Settings.Home" parent="Theme.Settings.HomeBase">
<item name="colorPrimary">@*android:color/primary_device_default_settings</item>
<item name="colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
<item name="android:colorBackground">?android:attr/colorPrimaryDark</item>
<item name="android:colorBackground">@android:color/system_surface_container_dark</item>
<!-- Homepage should follow device default design, the values is same as device default theme.-->
<item name="android:navigationBarColor">@android:color/black</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
@@ -29,9 +29,9 @@
<style name="Theme.SubSettings" parent="Theme.SubSettings.Base"/>
<style name="Theme.AlertDialog.Base" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert">
<item name="colorAccent">@*android:color/accent_device_default_dark</item>
<item name="colorAccent">@android:color/system_primary_dark</item>
<item name="android:colorError">@color/settings_dialog_colorError</item>
<item name="android:colorBackground">@*android:color/surface_dark</item>
<item name="android:colorBackground">@android:color/system_surface_container_high_dark</item>
</style>
<style name="Theme.Panel.Material" parent="Theme.Panel" >

View File

@@ -171,9 +171,8 @@
<!-- Icon tint color for battery usage system icon -->
<color name="battery_usage_system_icon_color">?android:attr/textColorPrimary</color>
<!-- Power anomaly color for icons, button and text -->
<!-- Power anomaly color for icons, button -->
<color name="power_anomaly_app_warning_hint_color">#D56E0C</color>
<color name="power_anomaly_primary_button_text_color">#FFFFFF</color>
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#699FF3</color>

View File

@@ -115,9 +115,9 @@
</style>
<style name="Theme.AlertDialog.Base" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert">
<item name="colorAccent">@*android:color/accent_device_default_light</item>
<item name="colorAccent">@android:color/system_primary_light</item>
<item name="android:colorError">@color/settings_dialog_colorError</item>
<item name="android:colorBackground">@*android:color/surface_light</item>
<item name="android:colorBackground">@android:color/system_surface_container_high_light</item>
</style>
<style name="Theme.AlertDialog" parent="Theme.AlertDialog.Base">
@@ -204,6 +204,7 @@
<!-- Homepage should follow device default design, the values is same as device default theme.-->
<item name="android:navigationBarColor">@android:color/white</item>
<item name="android:statusBarColor">?attr/colorPrimaryDark</item>
<item name="android:colorBackground">@android:color/system_surface_container_light</item>
</style>
<style name="Theme.Settings.Home.NoAnimation">

View File

@@ -15,5 +15,5 @@ menghanli@google.com #{LAST_RESORT_SUGGESTION}
cipson@google.com #{LAST_RESORT_SUGGESTION}
# Partner-team files
per-file HapticFeedbackIntensityPreferenceController.java = michaelwr@google.com
per-file *Vibration* = michaelwr@google.com
per-file HapticFeedbackIntensityPreferenceController.java = file:platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
per-file *Vibration* = file:platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS

View File

@@ -83,11 +83,14 @@ public class VibrationMainSwitchPreferenceController extends SettingsMainSwitchP
@Override
public boolean setChecked(boolean isChecked) {
// The main switch change can be triggered by both the user click and the
// SettingsMainSwitchPreferenceController state change. Make sure we only do it once.
boolean wasChecked = isChecked();
boolean success = Settings.System.putInt(mContext.getContentResolver(),
VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY,
isChecked ? ON : OFF);
if (success && isChecked) {
if (success && !wasChecked && isChecked) {
// Play a haptic as preview for the main toggle only when touch feedback is enabled.
VibrationPreferenceConfig.playVibrationPreview(
mVibrator, VibrationAttributes.USAGE_TOUCH);

View File

@@ -182,8 +182,10 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment {
refreshPreferenceController(QuickSettingsShortcutOptionController.class);
}
PreferredShortcuts.updatePreferredShortcutsFromSettings(
getContext(), mShortcutTargets);
if (getContext() != null) {
PreferredShortcuts.updatePreferredShortcutsFromSettings(
getContext(), mShortcutTargets);
}
}
};
@@ -388,7 +390,7 @@ public class EditShortcutsPreferenceFragment extends DashboardFragment {
private void refreshPreferenceController(
Class<? extends AbstractPreferenceController> controllerClass) {
AbstractPreferenceController controller = use(controllerClass);
if (controller != null) {
if (controller != null && getPreferenceScreen() != null) {
controller.displayPreference(getPreferenceScreen());
if (!TextUtils.isEmpty(controller.getPreferenceKey())) {
controller.updateState(findPreference(controller.getPreferenceKey()));

View File

@@ -87,6 +87,11 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
implements LifecycleObserver {
public static final String ADD_SERVICE_DEVICE_CONFIG = "credential_manager_service_search_uri";
private static final String TAG = "CredentialManagerPreferenceController";
private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER";
private static final int MAX_SELECTABLE_PROVIDERS = 5;
/**
* In the settings logic we should hide the list of additional credman providers if there is no
* provider selected at the top. The current logic relies on checking whether the autofill
@@ -95,11 +100,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
*/
public static final String AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER = "credential-provider";
private static final String TAG = "CredentialManagerPreferenceController";
private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER";
private static final int MAX_SELECTABLE_PROVIDERS = 5;
private final PackageManager mPm;
private final List<CredentialProviderInfo> mServices;
private final Set<String> mEnabledPackageNames;
@@ -522,8 +522,13 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
// empty string.
String selectedAutofillProvider =
DefaultCombinedPicker.getSelectedAutofillProvider(mContext, getUser());
if (TextUtils.equals(
selectedAutofillProvider, AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER)) {
String credentialAutofillService = "";
if (android.service.autofill.Flags.autofillCredmanDevIntegration()) {
credentialAutofillService = getCredentialAutofillService(mContext, TAG);
}
if (TextUtils.equals(selectedAutofillProvider, credentialAutofillService)
|| TextUtils.equals(
selectedAutofillProvider, AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER)) {
selectedAutofillProvider = "";
}
@@ -679,6 +684,17 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
return (enabledAdditionalProviderCount + 1) >= MAX_SELECTABLE_PROVIDERS;
}
/** Gets the credential autofill service component name. */
public static String getCredentialAutofillService(Context context, String tag) {
try {
return context.getResources().getString(
com.android.internal.R.string.config_defaultCredentialManagerAutofillService);
} catch (Resources.NotFoundException e) {
Log.e(tag, "Failed to find credential autofill service.", e);
}
return "";
}
private CombiPreference addProviderPreference(
@NonNull Context prefContext,
@NonNull CharSequence title,

View File

@@ -16,6 +16,8 @@
package com.android.settings.applications.credentials;
import static com.android.settings.applications.credentials.CredentialManagerPreferenceController.getCredentialAutofillService;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -463,9 +465,13 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment {
private void setProviders(String autofillProvider, List<String> primaryCredManProviders) {
if (TextUtils.isEmpty(autofillProvider)) {
if (primaryCredManProviders.size() > 0) {
autofillProvider =
CredentialManagerPreferenceController
.AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER;
if (android.service.autofill.Flags.autofillCredmanDevIntegration()) {
autofillProvider = getCredentialAutofillService(getContext(), TAG);
} else {
autofillProvider =
CredentialManagerPreferenceController
.AUTOFILL_CREDMAN_ONLY_PROVIDER_PLACEHOLDER;
}
}
}

View File

@@ -595,6 +595,15 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
if (Utils.isMonkeyRunning()) {
return;
}
// Disabling developer options in page-agnostic mode isn't supported as device isn't in
// production state
if (Enable16kUtils.isPageAgnosticModeOn(getContext())) {
Enable16kUtils.showPageAgnosticWarning(getContext());
onDisableDevelopmentOptionsRejected();
return;
}
DevelopmentSettingsEnabler.setDevelopmentSettingsEnabled(getContext(), false);
final SystemPropPoker poker = SystemPropPoker.getInstance();
poker.blockPokes();

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 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;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import androidx.annotation.NonNull;
public class Enable16KBootReceiver extends BroadcastReceiver {
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
String action = intent.getAction();
if (!Intent.ACTION_BOOT_COMPLETED.equals(action)) {
return;
}
// Do nothing if device is not in page-agnostic mode
if (!Enable16kUtils.isPageAgnosticModeOn(context)) {
return;
}
// start a service to post persistent notification
Intent startNotificationIntent = new Intent(context, PageAgnosticNotificationService.class);
context.startServiceAsUser(startNotificationIntent, UserHandle.SYSTEM);
}
}

View File

@@ -16,10 +16,15 @@
package com.android.settings.development;
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -60,7 +65,10 @@ public class Enable16KOemUnlockDialog extends InstrumentedDialogFragment
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.confirm_oem_unlock_for_16k_title)
.setMessage(R.string.confirm_oem_unlock_for_16k_text)
.setMessage(
Html.fromHtml(
getString(R.string.confirm_oem_unlock_for_16k_text),
FROM_HTML_MODE_COMPACT))
.setPositiveButton(android.R.string.ok, this /* onClickListener */)
.create();
}
@@ -74,4 +82,11 @@ public class Enable16KOemUnlockDialog extends InstrumentedDialogFragment
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
}
@Override
public void onStart() {
super.onStart();
((TextView) getDialog().findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
}
}

View File

@@ -23,17 +23,11 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.SystemProperties;
import android.os.SystemUpdateManager;
import android.os.UpdateEngine;
import android.os.UpdateEngineStable;
import android.os.UpdateEngineStableCallback;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.oemlock.OemLockManager;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
@@ -59,7 +53,6 @@ import com.google.common.util.concurrent.MoreExecutors;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -80,10 +73,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
private static final String TAG = "Enable16kPages";
private static final String REBOOT_REASON = "toggle16k";
private static final String ENABLE_16K_PAGES = "enable_16k_pages";
@VisibleForTesting
static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled";
private static final int ENABLE_4K_PAGE_SIZE = 0;
private static final int ENABLE_16K_PAGE_SIZE = 1;
@@ -97,9 +86,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
private static final int OFFSET_TO_FILE_NAME = 30;
public static final String EXPERIMENTAL_UPDATE_TITLE = "Android 16K Kernel Experimental Update";
private static final long PAGE_SIZE = Os.sysconf(OsConstants._SC_PAGESIZE);
private static final int PAGE_SIZE_16KB = 16 * 1024;
private @NonNull DevelopmentSettingsDashboardFragment mFragment;
private boolean mEnable16k;
@@ -112,12 +98,12 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
@NonNull Context context, @NonNull DevelopmentSettingsDashboardFragment fragment) {
super(context);
this.mFragment = fragment;
mEnable16k = (PAGE_SIZE == PAGE_SIZE_16KB);
mEnable16k = Enable16kUtils.isUsing16kbPages();
}
@Override
public boolean isAvailable() {
return SystemProperties.getBoolean(DEV_OPTION_PROPERTY, false);
return Enable16kUtils.is16KbToggleAvailable();
}
@Override
@@ -129,12 +115,12 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
public boolean onPreferenceChange(Preference preference, Object newValue) {
mEnable16k = (Boolean) newValue;
// Prompt user to do oem unlock first
if (!isDeviceOEMUnlocked()) {
if (!Enable16kUtils.isDeviceOEMUnlocked(mContext)) {
Enable16KOemUnlockDialog.show(mFragment);
return false;
}
if (isDataf2fs()) {
if (!Enable16kUtils.isDataExt4()) {
EnableExt4WarningDialog.show(mFragment, this);
return false;
}
@@ -145,7 +131,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
@Override
public void updateState(Preference preference) {
int defaultOptionValue =
PAGE_SIZE == PAGE_SIZE_16KB ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE;
Enable16kUtils.isUsing16kbPages() ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE;
final int optionValue =
Settings.Global.getInt(
mContext.getContentResolver(),
@@ -169,7 +155,7 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
@Override
protected void onDeveloperOptionsSwitchEnabled() {
int currentStatus =
PAGE_SIZE == PAGE_SIZE_16KB ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE;
Enable16kUtils.isUsing16kbPages() ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE;
Settings.Global.putInt(
mContext.getContentResolver(), Settings.Global.ENABLE_16K_PAGES, currentStatus);
}
@@ -432,51 +418,6 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
return infoBundle;
}
private boolean isDataf2fs() {
try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) {
String line;
while ((line = br.readLine()) != null) {
final String[] fields = line.split(" ");
final String partition = fields[1];
final String fsType = fields[2];
if (partition.equals("/data") && fsType.equals("f2fs")) {
return true;
}
}
} catch (IOException e) {
Log.e(TAG, "Failed to read /proc/mounts");
displayToast(mContext.getString(R.string.format_ext4_failure_toast));
}
return false;
}
private boolean isDeviceOEMUnlocked() {
// OEM unlock is checked for bootloader, carrier and user. Check all three to ensure
// that device is unlocked and it is also allowed by user as well as carrier
final OemLockManager oemLockManager = mContext.getSystemService(OemLockManager.class);
final UserManager userManager = mContext.getSystemService(UserManager.class);
if (oemLockManager == null || userManager == null) {
Log.e(TAG, "Required services not found on device to check for OEM unlock state.");
return false;
}
// If either of device or carrier is not allowed to unlock, return false
if (!oemLockManager.isDeviceOemUnlocked()
|| !oemLockManager.isOemUnlockAllowedByCarrier()) {
Log.e(TAG, "Device is not OEM unlocked or it is not allowed by carrier");
return false;
}
final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle)) {
Log.e(TAG, "Factory reset is not allowed for user.");
return false;
}
return true;
}
// if BOARD_16K_OTA_MOVE_VENDOR, OTAs will be present on the /vendor partition
private File getOtaFile() throws FileNotFoundException {
String otaPath = mEnable16k ? OTA_16K_PATH : OTA_4K_PATH;

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 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;
import android.content.Context;
import android.content.Intent;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.oemlock.OemLockManager;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Enable16kUtils {
private static final long PAGE_SIZE = Os.sysconf(OsConstants._SC_PAGESIZE);
private static final int PAGE_SIZE_16KB = 16 * 1024;
@VisibleForTesting
static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled";
private static final String TAG = "Enable16kUtils";
/**
* @param context uses context to retrieve OEM unlock info
* @return true if device is OEM unlocked and factory reset is allowed for user.
*/
public static boolean isDeviceOEMUnlocked(@NonNull Context context) {
// OEM unlock is checked for bootloader, carrier and user. Check all three to ensure
// that device is unlocked and it is also allowed by user as well as carrier
final OemLockManager oemLockManager = context.getSystemService(OemLockManager.class);
final UserManager userManager = context.getSystemService(UserManager.class);
if (oemLockManager == null || userManager == null) {
Log.e(TAG, "Required services not found on device to check for OEM unlock state.");
return false;
}
// If either of device or carrier is not allowed to unlock, return false
if (!oemLockManager.isDeviceOemUnlocked()) {
Log.e(TAG, "Device is not OEM unlocked");
return false;
}
final UserHandle userHandle = UserHandle.of(UserHandle.myUserId());
if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_FACTORY_RESET, userHandle)) {
Log.e(TAG, "Factory reset is not allowed for user.");
return false;
}
return true;
}
/**
* @return true if /data partition is ext4
*/
public static boolean isDataExt4() {
try (BufferedReader br = new BufferedReader(new FileReader("/proc/mounts"))) {
String line;
while ((line = br.readLine()) != null) {
Log.i(TAG, line);
final String[] fields = line.split(" ");
final String partition = fields[1];
final String fsType = fields[2];
if (partition.equals("/data") && fsType.equals("ext4")) {
return true;
}
}
} catch (IOException e) {
Log.e(TAG, "Failed to read /proc/mounts");
}
return false;
}
/**
* @return returns true if 16KB developer option is available for the device.
*/
public static boolean is16KbToggleAvailable() {
return SystemProperties.getBoolean(DEV_OPTION_PROPERTY, false);
}
/**
* 16kB page-agnostic mode requires /data to be ext4, ro.product.build.16k_page.enabled for
* device and Device OEM unlocked.
*
* @param context is needed to query OEM unlock state
* @return true if device is in page-agnostic mode.
*/
public static boolean isPageAgnosticModeOn(@NonNull Context context) {
return is16KbToggleAvailable() && isDeviceOEMUnlocked(context) && isDataExt4();
}
/**
* @return returns true if current page size is 16KB
*/
public static boolean isUsing16kbPages() {
return PAGE_SIZE == PAGE_SIZE_16KB;
}
/**
* show page-agnostic mode warning dialog to user
* @param context to start activity
*/
public static void showPageAgnosticWarning(@NonNull Context context) {
Intent intent = new Intent(context, PageAgnosticWarningActivity.class);
context.startActivityAsUser(intent, UserHandle.SYSTEM);
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 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;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
public class PageAgnosticNotificationService extends Service {
private static final String NOTIFICATION_CHANNEL_ID =
"com.android.settings.development.PageAgnosticNotificationService";
private static final int NOTIFICATION_ID = 1;
static final int DISABLE_UPDATES_SETTING = 1;
private NotificationManager mNotificationManager;
@Nullable
@Override
public IBinder onBind(@NonNull Intent intent) {
return null;
}
// create a notification channel to post persistent notification
private void createNotificationChannel() {
NotificationChannel channel =
new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
getString(R.string.page_agnostic_notification_channel_name),
NotificationManager.IMPORTANCE_HIGH);
mNotificationManager = getSystemService(NotificationManager.class);
if (mNotificationManager != null) {
mNotificationManager.createNotificationChannel(channel);
}
}
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel();
}
private Notification buildNotification() {
// Get the title and text according to page size
boolean isIn16kbMode = Enable16kUtils.isUsing16kbPages();
String title =
isIn16kbMode
? getString(R.string.page_agnostic_16k_pages_title)
: getString(R.string.page_agnostic_4k_pages_title);
String text =
isIn16kbMode
? getString(R.string.page_agnostic_16k_pages_text_short)
: getString(R.string.page_agnostic_4k_pages_text_short);
Intent notifyIntent = new Intent(this, PageAgnosticWarningActivity.class);
// Set the Activity to start in a new, empty task.
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Create the PendingIntent.
PendingIntent notifyPendingIntent =
PendingIntent.getActivity(
this,
0,
notifyIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Notification.Action action =
new Notification.Action.Builder(
R.drawable.empty_icon,
getString(R.string.page_agnostic_notification_action),
notifyPendingIntent)
.build();
Notification.Builder builder =
new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle(title)
.setContentText(text)
.setOngoing(true)
.setSmallIcon(R.drawable.ic_settings_24dp)
.setStyle(new Notification.BigTextStyle().bigText(text))
.setContentIntent(notifyPendingIntent)
.addAction(action);
return builder.build();
}
private void disableAutomaticUpdates() {
final int currentState =
Settings.Global.getInt(
getApplicationContext().getContentResolver(),
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
0 /* default */);
// 0 means enabled, 1 means disabled
if (currentState == 0) {
// automatic updates are enabled, disable them
Settings.Global.putInt(
getApplicationContext().getContentResolver(),
Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE,
DISABLE_UPDATES_SETTING);
}
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Notification notification = buildNotification();
if (mNotificationManager != null) {
mNotificationManager.notify(NOTIFICATION_ID, notification);
}
// No updates should be allowed in page-agnostic mode
disableAutomaticUpdates();
return Service.START_NOT_STICKY;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 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;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.android.settings.R;
public class PageAgnosticWarningActivity extends Activity {
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
String title =
Enable16kUtils.isUsing16kbPages()
? getString(R.string.page_agnostic_16k_pages_title)
: getString(R.string.page_agnostic_4k_pages_title);
String warningText =
Enable16kUtils.isUsing16kbPages()
? getString(R.string.page_agnostic_16k_pages_text)
: getString(R.string.page_agnostic_4k_pages_text);
showWarningDialog(title, warningText);
}
// Create warning dialog and make links clickable
private void showWarningDialog(String title, String warningText) {
AlertDialog dialog =
new AlertDialog.Builder(this)
.setTitle(title)
.setMessage(Html.fromHtml(warningText, Html.FROM_HTML_MODE_COMPACT))
.setCancelable(false)
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(
@NonNull DialogInterface dialog, int which) {
dialog.cancel();
finish();
}
})
.create();
dialog.show();
((TextView) dialog.findViewById(android.R.id.message))
.setMovementMethod(LinkMovementMethod.getInstance());
}
}

View File

@@ -78,6 +78,21 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
}
private CharSequence generateLabel(BatteryInfo info) {
if (Utils.containsIncompatibleChargers(mContext, TAG)) {
return mContext.getString(
com.android.settingslib.R.string.battery_info_status_not_charging);
}
if (BatteryUtils.isBatteryDefenderOn(info)) {
return mContext.getString(
com.android.settingslib.R.string.battery_info_status_charging_on_hold);
}
if (info.remainingLabel == null
|| info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
return info.statusLabel;
}
if (mBatterySettingsFeatureProvider.isChargingOptimizationMode(mContext)) {
return info.remainingLabel;
}
if (info.pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
final CharSequence wirelessChargingLabel =
mBatterySettingsFeatureProvider.getWirelessChargingLabel(mContext, info);
@@ -85,18 +100,7 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
return wirelessChargingLabel;
}
}
if (Utils.containsIncompatibleChargers(mContext, TAG)) {
return mContext.getString(
com.android.settingslib.R.string.battery_info_status_not_charging);
} else if (BatteryUtils.isBatteryDefenderOn(info)) {
return mContext.getString(
com.android.settingslib.R.string.battery_info_status_charging_on_hold);
} else if (info.remainingLabel == null
|| info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
// Present status only if no remaining time or status anomalous
return info.statusLabel;
} else if (info.statusLabel != null && !info.discharging) {
if (info.statusLabel != null && !info.discharging) {
// Charging state
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
return info.isFastCharging

View File

@@ -319,6 +319,12 @@ public class BatteryInfo {
info.isFastCharging =
BatteryStatus.getChargingSpeed(context, batteryBroadcast)
== BatteryStatus.CHARGING_FAST;
if (info.isBatteryDefender) {
info.isBatteryDefender =
FeatureFactory.getFeatureFactory()
.getPowerUsageFeatureProvider()
.isBatteryDefend(info);
}
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {
@@ -388,9 +394,10 @@ public class BatteryInfo {
&& status != BatteryManager.BATTERY_STATUS_FULL
&& dockDefenderMode == BatteryUtils.DockDefenderMode.DISABLED)
|| dockDefenderMode == BatteryUtils.DockDefenderMode.TEMPORARILY_BYPASSED) {
final BatterySettingsFeatureProvider featureProvider =
FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider();
// Battery is charging to full
info.remainingTimeUs = PowerUtil.convertMsToUs(chargeTimeMs);
int resId = getChargingDurationResId(info.isFastCharging);
info.remainingLabel =
chargeTimeMs <= 0
@@ -400,7 +407,8 @@ public class BatteryInfo {
chargeTimeMs,
info.isFastCharging,
info.pluggedStatus,
currentTimeMs);
currentTimeMs,
featureProvider);
info.chargeLabel =
chargeTimeMs <= 0
@@ -411,7 +419,8 @@ public class BatteryInfo {
info.batteryPercentString,
chargeTimeMs,
info.isFastCharging,
currentTimeMs);
currentTimeMs,
featureProvider);
} else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) {
// Dock defender will be triggered in the future, charging will be optimized.
info.chargeLabel =
@@ -436,10 +445,17 @@ public class BatteryInfo {
long chargeRemainingTimeMs,
boolean isFastCharging,
int pluggedStatus,
long currentTimeMs) {
long currentTimeMs,
BatterySettingsFeatureProvider featureProvider) {
if (featureProvider.isChargingOptimizationMode(context)) {
final CharSequence chargingOptimizationRemainingLabel =
featureProvider.getChargingOptimizationRemainingLabel(
context, chargeRemainingTimeMs, currentTimeMs);
if (chargingOptimizationRemainingLabel != null) {
return chargingOptimizationRemainingLabel;
}
}
if (pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
BatterySettingsFeatureProvider featureProvider =
FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider();
final CharSequence wirelessChargingRemainingLabel =
featureProvider.getWirelessChargingRemainingLabel(
context, chargeRemainingTimeMs, currentTimeMs);
@@ -472,7 +488,16 @@ public class BatteryInfo {
String batteryPercentString,
long chargeTimeMs,
boolean isFastCharging,
long currentTimeMs) {
long currentTimeMs,
BatterySettingsFeatureProvider featureProvider) {
if (featureProvider.isChargingOptimizationMode(context)) {
final CharSequence chargingOptimizationChargeLabel =
featureProvider.getChargingOptimizationChargeLabel(
context, batteryPercentString, chargeTimeMs, currentTimeMs);
if (chargingOptimizationChargeLabel != null) {
return chargingOptimizationChargeLabel;
}
}
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
var timeString =
PowerUtil.getTargetTimeShortString(context, chargeTimeMs, currentTimeMs);

View File

@@ -53,4 +53,20 @@ public interface BatterySettingsFeatureProvider {
@Nullable
CharSequence getWirelessChargingRemainingLabel(
@NonNull Context context, long remainingTimeMs, long currentTimeMs);
/** Return true if it's in the charging optimization mode. */
boolean isChargingOptimizationMode(@NonNull Context context);
/** Return a charging remaining time label for charging optimization mode. */
@Nullable
CharSequence getChargingOptimizationRemainingLabel(
@NonNull Context context, long chargeRemainingTimeMs, long currentTimeMs);
/** Return a charge label for charging optimization mode. */
@Nullable
CharSequence getChargingOptimizationChargeLabel(
@NonNull Context context,
@NonNull String batteryPercentageString,
long chargeRemainingTimeMs,
long currentTimeMs);
}

View File

@@ -67,4 +67,26 @@ public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatur
@NonNull Context context, long remainingTimeMs, long currentTimeMs) {
return null;
}
@Override
public boolean isChargingOptimizationMode(@NonNull Context context) {
return false;
}
@Nullable
@Override
public CharSequence getChargingOptimizationRemainingLabel(
@NonNull Context context, long chargeRemainingTimeMs, long currentTimeMs) {
return null;
}
@Nullable
@Override
public CharSequence getChargingOptimizationChargeLabel(
@NonNull Context context,
@NonNull String batteryPercentageString,
long chargeRemainingTimeMs,
long currentTimeMs) {
return null;
}
}

View File

@@ -47,7 +47,7 @@ class MobileNetworkListFragment : DashboardFragment() {
super.onCreate(icicle)
if (Flags.isDualSimOnboardingEnabled()) {
context?.startSpaActivity(NetworkCellularGroupProvider.name);
context?.startSpaActivity(NetworkCellularGroupProvider.fileName)
finish()
}
}

View File

@@ -1,57 +0,0 @@
/*
* 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.settings.network.helper;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* This is a Callable class which queries valid card ID for eSIM
*/
public class QueryEsimCardId implements Callable<AtomicIntegerArray> {
private static final String TAG = "QueryEsimCardId";
private TelephonyManager mTelephonyManager;
/**
* Constructor of class
* @param TelephonyManager
*/
public QueryEsimCardId(TelephonyManager telephonyManager) {
mTelephonyManager = telephonyManager;
}
/**
* Implementation of Callable
* @return card ID(s) in AtomicIntegerArray
*/
public AtomicIntegerArray call() {
List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
if (cardInfos == null) {
return new AtomicIntegerArray(0);
}
return new AtomicIntegerArray(cardInfos.stream()
.filter(Objects::nonNull)
.filter(cardInfo -> (!cardInfo.isRemovable() && (cardInfo.getCardId() >= 0)))
.mapToInt(UiccCardInfo::getCardId)
.toArray());
}
}

View File

@@ -24,7 +24,6 @@ import android.util.Log;
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
import com.android.settings.network.helper.SubscriptionAnnotation;
import com.android.settingslib.utils.ThreadUtils;
import java.util.Collections;
@@ -103,15 +102,6 @@ public class SelectableSubscriptions implements Callable<List<SubscriptionAnnota
TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class);
try {
// query in background thread
Future<AtomicIntegerArray> eSimCardId =
ThreadUtils.postOnBackgroundThread(new QueryEsimCardId(telMgr));
// query in background thread
Future<AtomicIntegerArray> simSlotIndex =
ThreadUtils.postOnBackgroundThread(
new QuerySimSlotIndex(telMgr, true, true));
// query in background thread
Future<AtomicIntegerArray> activeSimSlotIndex =
ThreadUtils.postOnBackgroundThread(
@@ -120,16 +110,13 @@ public class SelectableSubscriptions implements Callable<List<SubscriptionAnnota
List<SubscriptionInfo> subInfoList = mSubscriptions.get();
// wait for result from background thread
List<Integer> eSimCardIdList = atomicToList(eSimCardId.get());
List<Integer> simSlotIndexList = atomicToList(simSlotIndex.get());
List<Integer> activeSimSlotIndexList = atomicToList(activeSimSlotIndex.get());
// build a list of SubscriptionAnnotation
return IntStream.range(0, subInfoList.size())
.mapToObj(subInfoIndex ->
new SubscriptionAnnotation.Builder(subInfoList, subInfoIndex))
.map(annoBdr -> annoBdr.build(mContext,
eSimCardIdList, simSlotIndexList, activeSimSlotIndexList))
.map(annoBdr -> annoBdr.build(mContext, activeSimSlotIndexList))
.filter(mFilter)
.collect(Collectors.collectingAndThen(Collectors.toList(), mFinisher));
} catch (Exception exception) {

View File

@@ -16,7 +16,6 @@
package com.android.settings.network.helper;
import android.content.Context;
import android.os.ParcelUuid;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -35,14 +34,11 @@ public class SubscriptionAnnotation {
private static final String TAG = "SubscriptionAnnotation";
private SubscriptionInfo mSubInfo;
private int mOrderWithinList;
private int mType = TYPE_UNKNOWN;
private boolean mIsExisted;
private boolean mIsActive;
private boolean mIsAllowToDisplay;
public static final ParcelUuid EMPTY_UUID = ParcelUuid.fromString("0-0-0-0-0");
public static final int TYPE_UNKNOWN = 0x0;
public static final int TYPE_PSIM = 0x1;
public static final int TYPE_ESIM = 0x2;
@@ -52,8 +48,8 @@ public class SubscriptionAnnotation {
*/
public static class Builder {
private List<SubscriptionInfo> mSubInfoList;
private int mIndexWithinList;
private final List<SubscriptionInfo> mSubInfoList;
private final int mIndexWithinList;
/**
* Constructor of builder
@@ -65,10 +61,9 @@ public class SubscriptionAnnotation {
mIndexWithinList = indexWithinList;
}
public SubscriptionAnnotation build(Context context, List<Integer> eSimCardId,
List<Integer> simSlotIndex, List<Integer> activeSimSlotIndex) {
public SubscriptionAnnotation build(Context context, List<Integer> activeSimSlotIndex) {
return new SubscriptionAnnotation(mSubInfoList, mIndexWithinList, context,
eSimCardId, simSlotIndex, activeSimSlotIndex);
activeSimSlotIndex);
}
}
@@ -78,8 +73,7 @@ public class SubscriptionAnnotation {
@Keep
@VisibleForTesting
protected SubscriptionAnnotation(List<SubscriptionInfo> subInfoList, int subInfoIndex,
Context context, List<Integer> eSimCardId,
List<Integer> simSlotIndex, List<Integer> activeSimSlotIndexList) {
Context context, List<Integer> activeSimSlotIndexList) {
if ((subInfoIndex < 0) || (subInfoIndex >= subInfoList.size())) {
return;
}
@@ -88,7 +82,6 @@ public class SubscriptionAnnotation {
return;
}
mOrderWithinList = subInfoIndex;
mType = mSubInfo.isEmbedded() ? TYPE_ESIM : TYPE_PSIM;
mIsExisted = true;
if (mType == TYPE_ESIM) {
@@ -104,12 +97,6 @@ public class SubscriptionAnnotation {
mIsAllowToDisplay = isDisplayAllowed(context);
}
// the index provided during construction of Builder
@Keep
public int getOrderingInList() {
return mOrderWithinList;
}
// type of subscription
@Keep
public int getType() {
@@ -141,12 +128,6 @@ public class SubscriptionAnnotation {
mSubInfo.getSubscriptionId();
}
// the grouping UUID
@Keep
public ParcelUuid getGroupUuid() {
return (mSubInfo == null) ? null : mSubInfo.getGroupUuid();
}
// the SubscriptionInfo
@Keep
public SubscriptionInfo getSubInfo() {

View File

@@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
@@ -50,8 +51,12 @@ class CallStateRepository(private val context: Context) {
fun isInCallFlow(): Flow<Boolean> = context.subscriptionsChangedFlow()
.flatMapLatest {
val subIds = subscriptionManager.activeSubscriptionIdList
combine(subIds.map(::callStateFlow)) { states ->
states.any { it != TelephonyManager.CALL_STATE_IDLE }
if (subIds.isEmpty()) {
flowOf(false)
} else {
combine(subIds.map(::callStateFlow)) { states ->
states.any { it != TelephonyManager.CALL_STATE_IDLE }
}
}
}
.conflate()

View File

@@ -50,7 +50,14 @@ public class PanelFeatureProviderImpl implements PanelFeatureProvider {
context.sendBroadcast(intent);
return null;
case Settings.Panel.ACTION_NFC:
return NfcPanel.create(context);
if (Flags.slicesRetirement()) {
Intent nfcIntent = new Intent(Settings.ACTION_NFC_SETTINGS);
nfcIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(nfcIntent);
return null;
} else {
return NfcPanel.create(context);
}
case Settings.Panel.ACTION_WIFI:
if (Flags.slicesRetirement()) {
Intent wifiIntent = new Intent(Settings.ACTION_WIFI_SETTINGS);

View File

@@ -31,10 +31,10 @@ import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.util.Log;
import com.android.settings.flags.Flags;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.UiccSlotUtil;
import com.android.settings.network.UiccSlotsException;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
import com.android.settings.sim.ChooseSimActivity;
import com.android.settings.sim.DsdsDialogActivity;
import com.android.settings.sim.SimActivationNotifier;
@@ -86,11 +86,6 @@ public class SimSlotChangeHandler {
throw new IllegalStateException("Cannot be called from main thread.");
}
if (mTelMgr.getActiveModemCount() > 1 && !isMultipleEnabledProfilesSupported()) {
Log.i(TAG, "The device is already in DSDS mode and no MEP. Do nothing.");
return;
}
UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
if (removableSlotInfo == null) {
Log.e(TAG, "Unable to find the removable slot. Do nothing.");
@@ -112,12 +107,25 @@ public class SimSlotChangeHandler {
// Sets the current removable slot state.
setRemovableSimSlotState(mContext, currentRemovableSlotState);
if (mTelMgr.getActiveModemCount() > 1 && isMultipleEnabledProfilesSupported()) {
if(!isRemovableSimInserted) {
Log.i(TAG, "Removable Sim is not inserted in DSDS mode and MEP. Do nothing.");
if (mTelMgr.getActiveModemCount() > 1) {
if (!Flags.isDualSimOnboardingEnabled() && !isMultipleEnabledProfilesSupported()) {
Log.d(TAG, "The device is already in DSDS mode and no MEP. Do nothing.");
return;
}
handleRemovableSimInsertUnderDsdsMep(removableSlotInfo);
if (!isRemovableSimInserted) {
Log.d(TAG, "Removable Sim is not inserted in DSDS mode. Do nothing.");
return;
}
boolean isDdsInvalidForNewUi = Flags.isDualSimOnboardingEnabled()
&& SubscriptionManager.getDefaultDataSubscriptionId()
== SubscriptionManager.INVALID_SUBSCRIPTION_ID;
if (isDdsInvalidForNewUi) {
handleRemovableSimInsertWhenDsdsAndNoDds();
} else if (isMultipleEnabledProfilesSupported()) {
handleRemovableSimInsertUnderDsdsMep(removableSlotInfo);
return;
}
Log.d(TAG, "the device is already in DSDS mode and have the DDS. Do nothing.");
return;
}
@@ -164,8 +172,8 @@ public class SimSlotChangeHandler {
Log.i(
TAG,
"Both removable SIM and eSIM are present. DSDS condition doesn't"
+ " satisfied. User inserted pSIM during SUW. Show choose SIM"
+ " screen.");
+ " satisfied. User inserted pSIM during SUW. Show choose SIM"
+ " screen.");
startChooseSimActivity(true);
}
} else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_REMOVE) {
@@ -232,8 +240,8 @@ public class SimSlotChangeHandler {
if (groupedEmbeddedSubscriptions.size() == 0 || !removableSlotInfo.getPorts().stream()
.findFirst().get().isActive()) {
Log.i(TAG, "eSIM slot is active or no subscriptions exist. Do nothing."
+ " The removableSlotInfo: " + removableSlotInfo
+ ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions);
+ " The removableSlotInfo: " + removableSlotInfo
+ ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions);
return;
}
@@ -251,6 +259,17 @@ public class SimSlotChangeHandler {
startChooseSimActivity(false);
}
private void handleRemovableSimInsertWhenDsdsAndNoDds() {
List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
if (subscriptionInfos.isEmpty()) {
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
return;
}
Log.d(TAG, "isDdsInvalidForNewUi and getAvailableRemovableSubscription:"
+ subscriptionInfos);
startSimConfirmDialogActivity(subscriptionInfos.get(0).getSubscriptionId());
}
private void handleRemovableSimInsertUnderDsdsMep(UiccSlotInfo removableSlotInfo) {
Log.i(TAG, "Handle Removable SIM inserted under DSDS+Mep.");
@@ -309,7 +328,7 @@ public class SimSlotChangeHandler {
try {
// DEVICE_PROVISIONED is 0 if still in setup wizard. 1 if setup completed.
return Settings.Global.getInt(
context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED)
context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED)
== 1;
} catch (Settings.SettingNotFoundException e) {
Log.e(TAG, "Cannot get DEVICE_PROVISIONED from the device.", e);

View File

@@ -118,7 +118,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
ApnEditPageProvider,
SimOnboardingPageProvider,
BatteryOptimizationModeAppListPageProvider,
NetworkCellularGroupProvider,
NetworkCellularGroupProvider(),
WifiPrivacyPageProvider,
)

View File

@@ -94,9 +94,7 @@ class InstallUnknownAppsListModel(private val context: Context) :
private fun isChangeable(
record: InstallUnknownAppsRecord,
potentialPackageNames: Set<String>,
) =
record.appOpsController.getMode() != MODE_DEFAULT ||
record.app.packageName in potentialPackageNames
) = record.app.packageName in potentialPackageNames
private fun getPotentialPackageNames(userId: Int): Set<String> =
AppGlobals.getPackageManager()

View File

@@ -75,8 +75,8 @@ import kotlinx.coroutines.withContext
/**
* Showing the sim onboarding which is the process flow of sim switching on.
*/
object NetworkCellularGroupProvider : SettingsPageProvider {
override val name = "NetworkCellularGroupProvider"
open class NetworkCellularGroupProvider : SettingsPageProvider {
override val name = fileName
private val owner = createSettingsPage()
@@ -85,7 +85,7 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
var defaultDataSubId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
var nonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
open fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
// never using
Preference(object : PreferenceModel {
@@ -123,13 +123,26 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
nonDdsRemember.intValue = nonDds
}
PageImpl(
subscriptionViewModel.selectableSubscriptionInfoListFlow,
callsSelectedId,
textsSelectedId,
mobileDataSelectedId,
nonDdsRemember
)
val selectableSubscriptionInfoList by subscriptionViewModel
.selectableSubscriptionInfoListFlow
.collectAsStateWithLifecycle(initialValue = emptyList())
val stringSims = stringResource(R.string.provider_network_settings_title)
RegularScaffold(title = stringSims) {
SimsSection(selectableSubscriptionInfoList)
MobileDataSectionImpl(mobileDataSelectedId,
nonDdsRemember,
)
PrimarySimSectionImpl(
subscriptionViewModel.selectableSubscriptionInfoListFlow,
callsSelectedId,
textsSelectedId,
mobileDataSelectedId,
)
OtherSection()
}
}
private fun allOfFlows(context: Context,
@@ -139,7 +152,7 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
context.defaultVoiceSubscriptionFlow(),
context.defaultSmsSubscriptionFlow(),
context.defaultDefaultDataSubscriptionFlow(),
NetworkCellularGroupProvider::refreshUiStates,
this::refreshUiStates,
).flowOn(Dispatchers.Default)
private fun refreshUiStates(
@@ -164,32 +177,12 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
Log.d(name, "defaultDataSubId: $defaultDataSubId, nonDds: $nonDds")
}
}
@Composable
fun PageImpl(
selectableSubscriptionInfoListFlow: StateFlow<List<SubscriptionInfo>>,
defaultVoiceSubId: MutableIntState,
defaultSmsSubId: MutableIntState,
defaultDataSubId: MutableIntState,
nonDds: MutableIntState,
) {
val selectableSubscriptionInfoList by selectableSubscriptionInfoListFlow
.collectAsStateWithLifecycle(initialValue = emptyList())
val stringSims = stringResource(R.string.provider_network_settings_title)
RegularScaffold(title = stringSims) {
SimsSection(selectableSubscriptionInfoList)
MobileDataSectionImpl(defaultDataSubId,
nonDds,
)
PrimarySimSectionImpl(
selectableSubscriptionInfoListFlow,
defaultVoiceSubId,
defaultSmsSubId,
defaultDataSubId,
)
@Composable
open fun OtherSection(){
// Do nothing
}
companion object {
const val fileName = "NetworkCellularGroupProvider"
}
}
@@ -417,9 +410,9 @@ suspend fun setMobileData(
enabled: Boolean,
): Unit =
withContext(Dispatchers.Default) {
Log.d(NetworkCellularGroupProvider.name, "setMobileData: $enabled")
Log.d(NetworkCellularGroupProvider.fileName, "setMobileData: $enabled")
if (enabled) {
Log.d(NetworkCellularGroupProvider.name, "setDefaultData: [$subId]")
Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$subId]")
subscriptionManager?.setDefaultDataSubId(subId)
}
TelephonyRepository(context)

View File

@@ -75,6 +75,7 @@ public class GuestTelephonyPreferenceController extends TogglePreferenceControll
super.updateState(preference);
mUserCaps.updateAddUserCapabilities(mContext);
preference.setVisible(isAvailable() && mUserCaps.mUserSwitcherEnabled
&& mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
&& mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
&& !UserManager.isHeadlessSystemUserMode());
}
}

View File

@@ -376,7 +376,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
if (!Utils.isVoiceCapable(context)) { // no telephony
removePreference(KEY_ENABLE_TELEPHONY);
}
if (mUserInfo.isMain()) {
if (mUserInfo.isMain() || UserManager.isHeadlessSystemUserMode()) {
removePreference(KEY_ENABLE_TELEPHONY);
}
if (mUserInfo.isRestricted()) {

View File

@@ -62,13 +62,13 @@ public class Enable16kPagesPreferenceControllerTest {
@Test
public void onSystemPropertyDisabled_shouldDisablePreference() {
SystemProperties.set(Enable16kPagesPreferenceController.DEV_OPTION_PROPERTY, "false");
SystemProperties.set(Enable16kUtils.DEV_OPTION_PROPERTY, "false");
assertThat(mController.isAvailable()).isEqualTo(false);
}
@Test
public void onSystemPropertyEnabled_shouldEnablePreference() {
SystemProperties.set(Enable16kPagesPreferenceController.DEV_OPTION_PROPERTY, "true");
SystemProperties.set(Enable16kUtils.DEV_OPTION_PROPERTY, "true");
assertThat(mController.isAvailable()).isEqualTo(true);
}

View File

@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
@@ -307,6 +308,9 @@ public class BatteryInfoTest {
@Test
public void getBatteryInfo_chargingWithDefender_updateChargeLabel() {
doReturn(TEST_CHARGE_TIME_REMAINING).when(mBatteryUsageStats).getChargeTimeRemainingMs();
doReturn(true)
.when(mFeatureFactory.powerUsageFeatureProvider)
.isBatteryDefend(any(BatteryInfo.class));
mChargingBatteryBroadcast.putExtra(
BatteryManager.EXTRA_CHARGING_STATUS,
BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE);
@@ -363,6 +367,9 @@ public class BatteryInfoTest {
.when(mBatteryUsageStats)
.getChargeTimeRemainingMs();
doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isExtraDefend();
doReturn(true)
.when(mFeatureFactory.powerUsageFeatureProvider)
.isBatteryDefend(any(BatteryInfo.class));
Intent intent =
createBatteryIntent(
BatteryManager.BATTERY_PLUGGED_DOCK,
@@ -709,6 +716,79 @@ public class BatteryInfoTest {
expectedChargeLabel);
}
@Test
public void getBatteryInfo_chargeOptimizationMode_updateRemainingAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65);
var expectedRemainingLabel = "Done charging by";
var expectedChargeLabel = "65% - " + expectedRemainingLabel;
when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext))
.thenReturn(true);
when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationRemainingLabel(
eq(mContext), anyLong(), anyLong()))
.thenReturn(expectedRemainingLabel);
when(mFeatureFactory.batterySettingsFeatureProvider.getChargingOptimizationChargeLabel(
eq(mContext), anyString(), anyLong(), anyLong()))
.thenReturn(expectedChargeLabel);
var expectedStatusLabel = "Charging";
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_notChargeOptimizationModeWithV1_updateRemainingAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
/* chargingStringV2Enabled= */ false);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65);
when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext))
.thenReturn(false);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "2 hr, 10 min left until full";
var expectedChargeLabel = "65% - " + expectedRemainingLabel;
assertGetBatteryInfo(
batteryIntent,
/* currentTimeMillis= */ UNUSED_TIME_MS,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
@Test
public void getBatteryInfo_notChargeOptimizationModeWithV2_updateRemainingAndStatusLabel() {
prepareTestGetBatteryInfoEnvironment(
/* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
/* chargingStringV2Enabled= */ true);
Intent batteryIntent =
createIntentForGetBatteryInfoTest(
ChargingType.WIRED, ChargingSpeed.REGULAR, /* batteryLevel= */ 65);
when(mFeatureFactory.batterySettingsFeatureProvider.isChargingOptimizationMode(mContext))
.thenReturn(false);
var expectedStatusLabel = "Charging";
var expectedRemainingLabel = "Fully charged by";
var expectedChargeLabel = "65% - " + expectedRemainingLabel;
var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
assertGetBatteryInfo(
batteryIntent,
currentTimeMillis,
expectedStatusLabel,
expectedRemainingLabel,
expectedChargeLabel);
}
private enum ChargingSpeed {
FAST,
REGULAR,

View File

@@ -79,4 +79,20 @@ public class BatterySettingsFeatureProviderImplTest {
public void getWirelessChargingRemainingLabel_returnNull() {
assertThat(mImpl.getWirelessChargingRemainingLabel(mContext, 1000L, 1000L)).isNull();
}
@Test
public void isChargingOptimizationMode_default_returnFalse() {
assertThat(mImpl.isChargingOptimizationMode(mContext)).isFalse();
}
@Test
public void getChargingOptimizationRemainingLabel_default_returnNull() {
assertThat(mImpl.getChargingOptimizationRemainingLabel(mContext, 1000L, 1000L)).isNull();
}
@Test
public void getChargingOptimizationChargeLabel_default_returnNull() {
assertThat(mImpl.getChargingOptimizationChargeLabel(mContext, "70%", 1000L, 1000L))
.isNull();
}
}

View File

@@ -35,6 +35,7 @@ import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class CallStateRepositoryTest {
@@ -86,6 +87,17 @@ class CallStateRepositoryTest {
.inOrder()
}
@Test
fun isInCallFlow_noActiveSubscription() = runBlocking {
mockSubscriptionManager.stub {
on { activeSubscriptionIdList } doReturn intArrayOf()
}
val isInCall = repository.isInCallFlow().firstWithTimeoutOrNull()
assertThat(isInCall).isFalse()
}
@Test
fun isInCallFlow_initial() = runBlocking {
val isInCall = repository.isInCallFlow().firstWithTimeoutOrNull()

View File

@@ -6,7 +6,9 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -15,64 +17,67 @@ import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public final class AppStateAppBatteryUsageBridgeTest {
private static final String TEST_PACKAGE_1 = "com.example.test.pkg1";
private static final String TEST_PACKAGE_2 = "com.example.test.pkg2";
private static final int UID_1 = 12345;
private static final int UID_2 = 7654321;
private static final String TEST_PACKAGE_1 = "com.example.test.pkg1";
private static final String TEST_PACKAGE_2 = "com.example.test.pkg2";
private static final int UID_1 = 12345;
private static final int UID_2 = 7654321;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private PowerAllowlistBackend mPowerAllowlistBackend;
private Context mContext;
private ApplicationInfo mApplicationInfo;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private PowerAllowlistBackend mPowerAllowlistBackend;
@Test
public void updateExtraInfo_updatesRestricted() {
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(false);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
UID_1, TEST_PACKAGE_1)).thenReturn(AppOpsManager.MODE_IGNORED);
AppStateAppBatteryUsageBridge bridge =
new AppStateAppBatteryUsageBridge(mContext, null, null);
bridge.mAppOpsManager = mAppOpsManager;
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
AppEntry entry = new AppEntry(mContext, null, 0);
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mApplicationInfo = new ApplicationInfo();
mApplicationInfo.sourceDir = "test_dir";
}
bridge.updateExtraInfo(entry, TEST_PACKAGE_1, UID_1);
@Test
public void updateExtraInfo_updatesRestricted() {
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(false);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
UID_1, TEST_PACKAGE_1)).thenReturn(AppOpsManager.MODE_IGNORED);
AppStateAppBatteryUsageBridge bridge =
new AppStateAppBatteryUsageBridge(mContext, null, null);
bridge.mAppOpsManager = mAppOpsManager;
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
AppEntry entry = new AppEntry(mContext, mApplicationInfo, 0);
assertThat(entry.extraInfo.getClass())
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_RESTRICTED);
}
bridge.updateExtraInfo(entry, TEST_PACKAGE_1, UID_1);
@Test
public void updateExtraInfo_updatesUnrestricted() {
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(true);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
UID_2, TEST_PACKAGE_2)).thenReturn(AppOpsManager.MODE_ALLOWED);
AppStateAppBatteryUsageBridge bridge =
new AppStateAppBatteryUsageBridge(mContext, null, null);
bridge.mAppOpsManager = mAppOpsManager;
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
AppEntry entry = new AppEntry(mContext, null, 0);
assertThat(entry.extraInfo.getClass())
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_RESTRICTED);
}
bridge.updateExtraInfo(entry, TEST_PACKAGE_2, UID_2);
@Test
public void updateExtraInfo_updatesUnrestricted() {
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_2, UID_2)).thenReturn(true);
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
UID_2, TEST_PACKAGE_2)).thenReturn(AppOpsManager.MODE_ALLOWED);
AppStateAppBatteryUsageBridge bridge =
new AppStateAppBatteryUsageBridge(mContext, null, null);
bridge.mAppOpsManager = mAppOpsManager;
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
AppEntry entry = new AppEntry(mContext, mApplicationInfo, 0);
assertThat(entry.extraInfo.getClass())
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_UNRESTRICTED);
}
bridge.updateExtraInfo(entry, TEST_PACKAGE_2, UID_2);
assertThat(entry.extraInfo.getClass())
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_UNRESTRICTED);
}
}