diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9ae77b24c15..14879e24ab2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -5192,6 +5192,18 @@ android:theme="@style/Theme.SpaLib.Dialog"> + + + + + + + + + settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingCallAudioPreferenceController" /> + android:title="@string/audio_streams_main_page_title" /> \ No newline at end of file diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 9ebc12454cf..3367bf18f9f 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -47,6 +47,7 @@ public class Settings extends SettingsActivity { public static class MemtagPageActivity extends SettingsActivity { /* empty */} public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ } + public static class BluetoothDashboardActivity extends SettingsActivity { /* empty */ } public static class CreateShortcutActivity extends SettingsActivity { /* empty */ } public static class FaceSettingsActivity extends SettingsActivity { /* empty */ } /** Container for {@link FaceSettings} to use with a pre-defined task affinity. */ diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 39b13133dd4..795f999a030 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -264,7 +264,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { mIsSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent()); if (mCanAssumeUdfps || mCanAssumeSfps) { - updateTitleAndDescription(); + updateTitleAndDescription(true); } else { setHeaderText(R.string.security_settings_fingerprint_enroll_repeat_title); } @@ -387,7 +387,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { protected void onStart() { super.onStart(); updateProgress(false /* animate */); - updateTitleAndDescription(); + updateTitleAndDescription(true); if (mRestoring) { startIconAnimation(); } @@ -491,12 +491,14 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { return new Intent(this, FingerprintEnrollFinish.class); } - private void updateTitleAndDescription() { + private void updateTitleAndDescription(boolean force) { if (mCanAssumeUdfps) { updateTitleAndDescriptionForUdfps(); return; } else if (mCanAssumeSfps) { - updateTitleAndDescriptionForSfps(); + if (force || mSfpsEnrollmentFeature.shouldUpdateTitleAndDescription()) { + updateTitleAndDescriptionForSfps(); + } return; } @@ -814,12 +816,15 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { public void onEnrollmentProgressChange(int steps, int remaining) { updateProgress(true /* animate */); final int percent = (int) (((float) (steps - remaining) / (float) steps) * 100); - if (mCanAssumeSfps && mIsAccessibilityEnabled) { - CharSequence announcement = getString( - R.string.security_settings_sfps_enroll_progress_a11y_message, percent); - announceEnrollmentProgress(announcement); + if (mCanAssumeSfps) { + mSfpsEnrollmentFeature.handleOnEnrollmentProgressChange(steps, remaining); + if (mIsAccessibilityEnabled) { + CharSequence announcement = getString( + R.string.security_settings_sfps_enroll_progress_a11y_message, percent); + announceEnrollmentProgress(announcement); + } } - updateTitleAndDescription(); + updateTitleAndDescription(false); animateFlash(); if (mCanAssumeUdfps) { if (mIsAccessibilityEnabled) { @@ -850,6 +855,9 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { if (mUdfpsEnrollHelper != null) { mUdfpsEnrollHelper.onAcquired(isAcquiredGood); } + if (mCanAssumeSfps) { + mSfpsEnrollmentFeature.handleOnAcquired(isAcquiredGood); + } } @Override diff --git a/src/com/android/settings/biometrics/fingerprint/MessageDisplayController.java b/src/com/android/settings/biometrics/fingerprint/MessageDisplayController.java index 4dc75b9d375..639bb3df497 100644 --- a/src/com/android/settings/biometrics/fingerprint/MessageDisplayController.java +++ b/src/com/android/settings/biometrics/fingerprint/MessageDisplayController.java @@ -177,6 +177,11 @@ public class MessageDisplayController extends FingerprintManager.EnrollmentCallb mEnrollmentCallback.onEnrollmentError(errMsgId, errString); } + @Override + public void onAcquired(boolean isAcquiredGood) { + mEnrollmentCallback.onAcquired(isAcquiredGood); + } + private Message getMessageToDisplay(long timeStamp) { ProgressMessage progressMessageToDisplay = getProgressMessageToDisplay(timeStamp); if (mMustDisplayProgress) { diff --git a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java index f99d394dceb..bdf9530828b 100644 --- a/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java +++ b/src/com/android/settings/biometrics/fingerprint/feature/SfpsEnrollmentFeature.java @@ -93,4 +93,25 @@ public interface SfpsEnrollmentFeature { * @param lottieView the view related to the lottie */ default void handleOnEnrollmentLottieComposition(LottieAnimationView lottieView) {} + + /** + * Indicates if the title and description should be updated. + * @return true to update the title and description; false otherwise. + */ + default boolean shouldUpdateTitleAndDescription() { + return true; + } + + /** + * Notifies an acquisition happens. + * @param isAcquiredGood isAcquiredGood + */ + default void handleOnAcquired(boolean isAcquiredGood) {} + + /** + * Notifies an enrollment progress changes event. + * @param steps steps + * @param remaining remaining + */ + default void handleOnEnrollmentProgressChange(int steps, int remaining) {} } diff --git a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragment.java similarity index 93% rename from src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java rename to src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragment.java index df94694b492..db82619ca60 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragment.java @@ -32,7 +32,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import java.util.List; /** Provides a dialog to choose the active device for calls and alarms. */ -public class CallsAndAlarmsDialogFragment extends InstrumentedDialogFragment { +public class AudioSharingCallAudioDialogFragment extends InstrumentedDialogFragment { private static final String TAG = "CallsAndAlarmsDialog"; private static final String BUNDLE_KEY_DEVICE_ITEMS = "bundle_key_device_items"; @@ -55,7 +55,7 @@ public class CallsAndAlarmsDialogFragment extends InstrumentedDialogFragment { } /** - * Display the {@link CallsAndAlarmsDialogFragment} dialog. + * Display the {@link AudioSharingCallAudioDialogFragment} dialog. * * @param host The Fragment this dialog will be hosted. * @param deviceItems The connected device items in audio sharing session. @@ -71,7 +71,8 @@ public class CallsAndAlarmsDialogFragment extends InstrumentedDialogFragment { if (manager.findFragmentByTag(TAG) == null) { final Bundle bundle = new Bundle(); bundle.putParcelableList(BUNDLE_KEY_DEVICE_ITEMS, deviceItems); - final CallsAndAlarmsDialogFragment dialog = new CallsAndAlarmsDialogFragment(); + final AudioSharingCallAudioDialogFragment dialog = + new AudioSharingCallAudioDialogFragment(); dialog.setArguments(bundle); dialog.show(manager, TAG); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java similarity index 96% rename from src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java rename to src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java index 8aaebc6dbd2..e848f88576b 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceController.java @@ -51,6 +51,8 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.utils.ThreadUtils; +import com.google.common.collect.ImmutableList; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -60,7 +62,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; /** PreferenceController to control the dialog to choose the active device for calls and alarms */ -public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController +public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController implements BluetoothCallback { private static final String TAG = "CallsAndAlarmsPreferenceController"; private static final String PREF_KEY = "calls_and_alarms"; @@ -131,7 +133,7 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen } }; - public CallsAndAlarmsPreferenceController(Context context) { + public AudioSharingCallAudioPreferenceController(Context context) { super(context, PREF_KEY); mBtManager = Utils.getLocalBtManager(mContext); mProfileManager = mBtManager == null ? null : mBtManager.getProfileManager(); @@ -176,16 +178,13 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen } updateDeviceItemsInSharingSession(); if (mDeviceItemsInSharingSession.size() >= 1) { - CallsAndAlarmsDialogFragment.show( + AudioSharingCallAudioDialogFragment.show( mFragment, mDeviceItemsInSharingSession, (AudioSharingDeviceItem item) -> { - if (!mGroupedConnectedDevices.containsKey( - item.getGroupId())) { - return; - } List devices = - mGroupedConnectedDevices.get(item.getGroupId()); + mGroupedConnectedDevices.getOrDefault( + item.getGroupId(), ImmutableList.of()); @Nullable CachedBluetoothDevice lead = AudioSharingUtils.getLeadDevice(devices); diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java index 275d1976086..c3248c7e573 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragment.java @@ -33,7 +33,7 @@ public class AudioSharingDashboardFragment extends DashboardFragment SettingsMainSwitchBar mMainSwitchBar; private AudioSharingSwitchBarController mSwitchBarController; private AudioSharingDeviceVolumeGroupController mAudioSharingDeviceVolumeGroupController; - private CallsAndAlarmsPreferenceController mCallsAndAlarmsPreferenceController; + private AudioSharingCallAudioPreferenceController mAudioSharingCallAudioPreferenceController; private AudioSharingPlaySoundPreferenceController mAudioSharingPlaySoundPreferenceController; private AudioStreamsCategoryController mAudioStreamsCategoryController; @@ -67,8 +67,9 @@ public class AudioSharingDashboardFragment extends DashboardFragment mAudioSharingDeviceVolumeGroupController = use(AudioSharingDeviceVolumeGroupController.class); mAudioSharingDeviceVolumeGroupController.init(this); - mCallsAndAlarmsPreferenceController = use(CallsAndAlarmsPreferenceController.class); - mCallsAndAlarmsPreferenceController.init(this); + mAudioSharingCallAudioPreferenceController = + use(AudioSharingCallAudioPreferenceController.class); + mAudioSharingCallAudioPreferenceController.init(this); mAudioSharingPlaySoundPreferenceController = use(AudioSharingPlaySoundPreferenceController.class); mAudioStreamsCategoryController = use(AudioStreamsCategoryController.class); @@ -100,7 +101,7 @@ public class AudioSharingDashboardFragment extends DashboardFragment private void updateVisibilityForAttachedPreferences() { mAudioSharingDeviceVolumeGroupController.updateVisibility(); - mCallsAndAlarmsPreferenceController.updateVisibility(); + mAudioSharingCallAudioPreferenceController.updateVisibility(); mAudioSharingPlaySoundPreferenceController.updateVisibility(); mAudioStreamsCategoryController.updateVisibility(); } diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java index 2e4930cf4b3..ea5eede9df9 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java @@ -42,6 +42,7 @@ import com.android.settings.bluetooth.Utils; import com.android.settings.connecteddevice.audiosharing.AudioSharingUtils; import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.VolumeControlProfile; @@ -60,6 +61,7 @@ public class AudioStreamMediaService extends Service { private static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action"; private static final String LEAVE_BROADCAST_TEXT = "Leave Broadcast"; private static final String CHANNEL_ID = "bluetooth_notification_channel"; + private static final String DEFAULT_DEVICE_NAME = ""; private static final int STATIC_PLAYBACK_DURATION = 100; private static final int STATIC_PLAYBACK_POSITION = 30; private static final int ZERO_PLAYBACK_SPEED = 0; @@ -355,16 +357,34 @@ public class AudioStreamMediaService extends Service { return mIsMuted ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build(); } + private String getDeviceName() { + if (mDevices == null || mDevices.isEmpty() || mLocalBtManager == null) { + return DEFAULT_DEVICE_NAME; + } + + CachedBluetoothDeviceManager manager = mLocalBtManager.getCachedDeviceManager(); + if (manager == null) { + return DEFAULT_DEVICE_NAME; + } + + CachedBluetoothDevice device = manager.findDevice(mDevices.get(0)); + return device != null ? device.getName() : DEFAULT_DEVICE_NAME; + } + private Notification buildNotification() { + String deviceName = getDeviceName(); + Notification.MediaStyle mediaStyle = + new Notification.MediaStyle() + .setMediaSession( + mLocalSession != null ? mLocalSession.getSessionToken() : null); + if (deviceName != null && !deviceName.isEmpty()) { + mediaStyle.setRemotePlaybackInfo( + deviceName, com.android.internal.R.drawable.ic_bt_headset_hfp, null); + } Notification.Builder notificationBuilder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing) - .setStyle( - new Notification.MediaStyle() - .setMediaSession( - mLocalSession != null - ? mLocalSession.getSessionToken() - : null)) + .setStyle(mediaStyle) .setContentText(this.getString(BROADCAST_CONTENT_TEXT)) .setSilent(true); return notificationBuilder.build(); diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java index 7c601c0d613..1c14712df30 100644 --- a/src/com/android/settings/core/gateway/SettingsGateway.java +++ b/src/com/android/settings/core/gateway/SettingsGateway.java @@ -86,6 +86,7 @@ import com.android.settings.bluetooth.BluetoothPairingDetail; import com.android.settings.bugreporthandler.BugReportHandlerPicker; import com.android.settings.communal.CommunalDashboardFragment; import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment; +import com.android.settings.connecteddevice.BluetoothDashboardFragment; import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment; import com.android.settings.connecteddevice.NfcAndPaymentFragment; import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment; @@ -213,6 +214,7 @@ public class SettingsGateway { AdvancedConnectedDeviceDashboardFragment.class.getName(), CreateShortcut.class.getName(), BluetoothPairingDetail.class.getName(), + BluetoothDashboardFragment.class.getName(), WifiNetworkDetailsFragment.class.getName(), ConfigureWifiSettings.class.getName(), SavedAccessPointsWifiSettings2.class.getName(), diff --git a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java index 99edbecd617..4c8b9b83cd1 100644 --- a/src/com/android/settings/fuelgauge/BatterySettingsStorage.java +++ b/src/com/android/settings/fuelgauge/BatterySettingsStorage.java @@ -57,7 +57,8 @@ import java.util.List; /** An implementation to backup and restore battery configurations. */ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage { - public static final String TAG = "BatteryBackupHelper"; + private static final String NAME = "BatteryBackupHelper"; + private static final String TAG = "BatterySettingsStorage"; // Definition for the device build information. public static final String KEY_BUILD_BRAND = "device_build_brand"; @@ -89,7 +90,7 @@ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage */ public static @NonNull BatterySettingsStorage get(@NonNull Context context) { return (BatterySettingsStorage) - BackupRestoreStorageManager.getInstance(context).getOrThrow(TAG); + BackupRestoreStorageManager.getInstance(context).getOrThrow(NAME); } public BatterySettingsStorage(@NonNull Context context) { @@ -99,7 +100,7 @@ public final class BatterySettingsStorage extends ObservableBackupRestoreStorage @NonNull @Override public String getName() { - return TAG; + return NAME; } @Override diff --git a/src/com/android/settings/network/MobileDataEnabledFlow.kt b/src/com/android/settings/network/MobileDataEnabledFlow.kt index 23423771f1f..1f995a94b0a 100644 --- a/src/com/android/settings/network/MobileDataEnabledFlow.kt +++ b/src/com/android/settings/network/MobileDataEnabledFlow.kt @@ -28,8 +28,8 @@ import kotlinx.coroutines.flow.merge * * Note: This flow can only notify enabled status changes, cannot provide the latest status. */ -fun Context.mobileDataEnabledFlow(subId: Int): Flow { - val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA) +fun Context.mobileDataEnabledFlow(subId: Int, sendInitialValue: Boolean = true): Flow { + val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA, sendInitialValue) return when (subId) { SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow else -> { diff --git a/src/com/android/settings/network/ProxySubscriptionManager.java b/src/com/android/settings/network/ProxySubscriptionManager.java index 7e276e889be..614491a5016 100644 --- a/src/com/android/settings/network/ProxySubscriptionManager.java +++ b/src/com/android/settings/network/ProxySubscriptionManager.java @@ -28,7 +28,6 @@ import android.telephony.SubscriptionManager; import android.util.Log; import androidx.annotation.Keep; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; @@ -242,15 +241,6 @@ public class ProxySubscriptionManager implements LifecycleObserver { return mSubscriptionMonitor.getAccessibleSubscriptionInfo(subId); } - /** - * Gets a list of active, visible subscription Id(s) of the currently active SIM(s). - * - * @return the list of subId's that are active and visible; the length may be 0. - */ - public @NonNull int[] getActiveSubscriptionIdList() { - return mSubscriptionMonitor.getActiveSubscriptionIdList(); - } - /** * Clear data cached within proxy */ diff --git a/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java b/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java index ef74a2ebdf0..fcbfdef4a72 100644 --- a/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java +++ b/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java @@ -19,20 +19,14 @@ package com.android.settings.network.telephony; import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; -import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Handler; import android.os.Looper; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.telephony.ims.ImsException; -import android.telephony.ims.ImsManager; -import android.telephony.ims.ImsMmTelManager; -import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.OnLifecycleEvent; import androidx.preference.Preference; @@ -40,15 +34,11 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.internal.annotations.VisibleForTesting; -import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.flags.Flags; import com.android.settings.network.MobileDataContentObserver; -import com.android.settings.network.ProxySubscriptionManager; import com.android.settings.network.SubscriptionsChangeListener; -import com.android.settings.network.ims.WifiCallingQueryImsState; -import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel; /** * Controls whether switch mobile data to the non-default SIM if the non-default SIM has better @@ -63,25 +53,29 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenceController implements LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient { - private static final String LOG_TAG = "AutoDataSwitchPrefCtrl"; + @Nullable private TwoStatePreference mPreference; + @Nullable private SubscriptionsChangeListener mChangeListener; + @Nullable private TelephonyManager mManager; + @Nullable private MobileDataContentObserver mMobileDataContentObserver; + @Nullable + private CrossSimCallingViewModel mCrossSimCallingViewModel; + @Nullable private PreferenceScreen mScreen; - private final MetricsFeatureProvider mMetricsFeatureProvider; - - public AutoDataSwitchPreferenceController(Context context, - String preferenceKey) { + public AutoDataSwitchPreferenceController( + @NonNull Context context, @NonNull String preferenceKey) { super(context, preferenceKey); - mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } - void init(int subId) { + void init(int subId, @Nullable CrossSimCallingViewModel crossSimCallingViewModel) { this.mSubId = subId; mManager = mContext.getSystemService(TelephonyManager.class).createForSubscriptionId(subId); + mCrossSimCallingViewModel = crossSimCallingViewModel; } @OnLifecycleEvent(ON_RESUME) @@ -121,35 +115,15 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); } - private int getOtherSubId(@NonNull int[] subIds) { - if (subIds.length > 1) { - for (int subId : subIds) { - if (subId != mSubId) { - return subId; - } - } - } - return SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - - private boolean isEnabled(int subId) { - if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return false; - } - TelephonyManager telephonyManager = mContext.getSystemService( - TelephonyManager.class).createForSubscriptionId(subId); - return telephonyManager != null && telephonyManager.isMobileDataPolicyEnabled( - TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH); - } - @Override public boolean setChecked(boolean isChecked) { - mManager.setMobileDataPolicyEnabled( - TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - isChecked); - if (mContext.getResources().getBoolean( - R.bool.config_auto_data_switch_enables_cross_sim_calling)) { - trySetCrossSimCalling(mContext, getActiveSubscriptionIdList(), isChecked /* enabled */); + if (mManager != null) { + mManager.setMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + isChecked); + } + if (mCrossSimCallingViewModel != null) { + mCrossSimCallingViewModel.updateCrossSimCalling(); } return true; } @@ -159,40 +133,6 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc return DataUsageUtils.hasMobileData(mContext); } - private boolean isCrossSimCallingAllowedByPlatform(Context context, int subId) { - if ((new WifiCallingQueryImsState(context, subId)).isWifiCallingSupported()) { - PersistableBundle bundle = getCarrierConfigForSubId(subId); - return (bundle != null) && bundle.getBoolean( - CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, - false /*default*/); - } - return false; - } - - protected ImsMmTelManager getImsMmTelManager(Context context, int subId) { - ImsManager imsMgr = context.getSystemService(ImsManager.class); - return (imsMgr == null) ? null : imsMgr.getImsMmTelManager(subId); - } - - private void trySetCrossSimCallingPerSub(Context context, int subId, boolean enabled) { - try { - getImsMmTelManager(context, subId).setCrossSimCallingEnabled(enabled); - } catch (ImsException | IllegalArgumentException | NullPointerException exception) { - Log.w(LOG_TAG, "failed to change cross SIM calling configuration to " + enabled - + " for subID " + subId + "with exception: ", exception); - } - } - - private void trySetCrossSimCalling(Context context, int[] subIds, boolean enabled) { - mMetricsFeatureProvider.action(mContext, - SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT, enabled); - for (int subId : subIds) { - if (isCrossSimCallingAllowedByPlatform(context, subId)) { - trySetCrossSimCallingPerSub(context, subId, enabled); - } - } - } - @Override public int getAvailabilityStatus(int subId) { if (Flags.isDualSimOnboardingEnabled() @@ -221,20 +161,11 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc updateState(mPreference); } - private int[] getActiveSubscriptionIdList() { - return ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionIdList(); - } - /** * Trigger displaying preference when Mobile data content changed. */ @VisibleForTesting public void refreshPreference() { - if (mContext.getResources().getBoolean( - R.bool.config_auto_data_switch_enables_cross_sim_calling)) { - int[] subIds = getActiveSubscriptionIdList(); - trySetCrossSimCalling(mContext, subIds, isEnabled(getOtherSubId(subIds))); - } if (mScreen != null) { super.displayPreference(mScreen); } diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index d57de1a788f..c9a2a0bf733 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -38,6 +38,7 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.Preference; import com.android.settings.R; @@ -53,6 +54,7 @@ import com.android.settings.network.telephony.cdma.CdmaSubscriptionPreferenceCon import com.android.settings.network.telephony.cdma.CdmaSystemSelectPreferenceController; import com.android.settings.network.telephony.gsm.AutoSelectPreferenceController; import com.android.settings.network.telephony.gsm.OpenNetworkSelectPagePreferenceController; +import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.wifi.WifiPickerTrackerHelper; import com.android.settingslib.core.AbstractPreferenceController; @@ -240,7 +242,9 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme use(CarrierSettingsVersionPreferenceController.class).init(mSubId); use(BillingCyclePreferenceController.class).init(mSubId); use(MmsMessagePreferenceController.class).init(mSubId); - use(AutoDataSwitchPreferenceController.class).init(mSubId); + final var crossSimCallingViewModel = + new ViewModelProvider(this).get(CrossSimCallingViewModel.class); + use(AutoDataSwitchPreferenceController.class).init(mSubId, crossSimCallingViewModel); use(DisabledSubscriptionController.class).init(mSubId); use(DeleteSimProfilePreferenceController.class).init(mSubId); use(DisableSimFooterPreferenceController.class).init(mSubId); diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt index 4fd28a20f93..3ee854843fc 100644 --- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt +++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt @@ -124,6 +124,14 @@ fun Context.getSelectableSubscriptionInfoList(): List { } } // Matching the sorting order in SubscriptionManagerService.getAvailableSubscriptionInfoList - .sortedWith(compareBy({ it.simSlotIndex }, { it.subscriptionId })) + .sortedWith(compareBy({ it.sortableSimSlotIndex }, { it.subscriptionId })) .also { Log.d(TAG, "getSelectableSubscriptionInfoList: $it") } } + +/** Subscription with invalid sim slot index has lowest sort order. */ +private val SubscriptionInfo.sortableSimSlotIndex: Int + get() = if (simSlotIndex != SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + simSlotIndex + } else { + Int.MAX_VALUE + } diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt index dd1cbd5bc11..9bc10e555e1 100644 --- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt +++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt @@ -51,6 +51,8 @@ interface ImsMmTelRepository { @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @AccessNetworkConstants.TransportType transportType: Int, ): Boolean + + suspend fun setCrossSimCallingEnabled(enabled: Boolean) } class ImsMmTelRepositoryImpl( @@ -130,6 +132,7 @@ class ImsMmTelRepositoryImpl( @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @AccessNetworkConstants.TransportType transportType: Int, ): Boolean = withContext(Dispatchers.Default) { + val logName = "isSupported(capability=$capability,transportType=$transportType)" suspendCancellableCoroutine { continuation -> try { imsMmTelManager.isSupported( @@ -140,9 +143,18 @@ class ImsMmTelRepositoryImpl( ) } catch (e: Exception) { continuation.resume(false) - Log.w(TAG, "[$subId] isSupported failed", e) + Log.w(TAG, "[$subId] $logName failed", e) } - }.also { Log.d(TAG, "[$subId] isSupported = $it") } + }.also { Log.d(TAG, "[$subId] $logName = $it") } + } + + override suspend fun setCrossSimCallingEnabled(enabled: Boolean) { + try { + imsMmTelManager.setCrossSimCallingEnabled(enabled) + Log.d(TAG, "[$subId] setCrossSimCallingEnabled: $enabled") + } catch (e: Exception) { + Log.e(TAG, "[$subId] failed setCrossSimCallingEnabled to $enabled", e) + } } private companion object { diff --git a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt new file mode 100644 index 00000000000..5dcac1ebaac --- /dev/null +++ b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt @@ -0,0 +1,119 @@ +/* + * 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.network.telephony.wificalling + +import android.app.Application +import android.app.settings.SettingsEnums +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import com.android.settings.R +import com.android.settings.network.mobileDataEnabledFlow +import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl +import com.android.settings.network.telephony.requireSubscriptionManager +import com.android.settings.network.telephony.safeGetConfig +import com.android.settings.network.telephony.subscriptionsChangedFlow +import com.android.settings.network.telephony.telephonyManager +import com.android.settings.overlay.FeatureFactory.Companion.featureFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.plus + +@OptIn(ExperimentalCoroutinesApi::class) +class CrossSimCallingViewModel( + private val application: Application, +) : AndroidViewModel(application) { + + private val subscriptionManager = application.requireSubscriptionManager() + private val carrierConfigManager = + application.getSystemService(CarrierConfigManager::class.java)!! + private val scope = viewModelScope + Dispatchers.Default + private val metricsFeatureProvider = featureFactory.metricsFeatureProvider + private val updateChannel = Channel() + + init { + val resources = application.resources + if (resources.getBoolean(R.bool.config_auto_data_switch_enables_cross_sim_calling)) { + application.subscriptionsChangedFlow() + .flatMapLatest { + val activeSubIds = subscriptionManager.activeSubscriptionIdList.toList() + merge( + activeSubIds.anyMobileDataEnableChangedFlow(), + updateChannel.receiveAsFlow(), + ).map { + activeSubIds to crossSimCallNewEnabled(activeSubIds) + } + } + .distinctUntilChanged() + .onEach { (activeSubIds, newEnabled) -> + updateCrossSimCalling(activeSubIds, newEnabled) + } + .launchIn(scope) + } + } + + fun updateCrossSimCalling() { + updateChannel.trySend(Unit) + } + + private fun List.anyMobileDataEnableChangedFlow() = map { subId -> + application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false) + }.merge() + + private suspend fun updateCrossSimCalling(activeSubIds: List, newEnabled: Boolean) { + metricsFeatureProvider.action( + application, + SettingsEnums.ACTION_UPDATE_CROSS_SIM_CALLING_ON_AUTO_DATA_SWITCH_EVENT, + newEnabled, + ) + activeSubIds.filter { crossSimAvailable(it) }.forEach { subId -> + ImsMmTelRepositoryImpl(application, subId) + .setCrossSimCallingEnabled(newEnabled) + } + } + + private suspend fun crossSimAvailable(subId: Int): Boolean = + WifiCallingRepository(application, subId).isWifiCallingSupported() && + crossSimImsAvailable(subId) + + private fun crossSimImsAvailable(subId: Int): Boolean = + carrierConfigManager.safeGetConfig( + keys = listOf(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL), + subId = subId, + ).getBoolean(CarrierConfigManager.KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL, false) + + private fun crossSimCallNewEnabled(activeSubscriptionIdList: List): Boolean { + val defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId() + return SubscriptionManager.isValidSubscriptionId(defaultDataSubId) && + activeSubscriptionIdList.any { subId -> + subId != defaultDataSubId && + application.telephonyManager(subId).isMobileDataPolicyEnabled( + TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH + ) + } + } +} diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt index ac95404e78e..a5d4ba832ab 100644 --- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt @@ -29,17 +29,19 @@ import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl import com.android.settings.network.telephony.ims.imsFeatureProvisionedFlow import com.android.settings.network.telephony.subscriptionsChangedFlow +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext class WifiCallingRepository( private val context: Context, private val subId: Int, - private val imsMmTelRepository : ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) + private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) ) { private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!! .createForSubscriptionId(subId) @@ -76,10 +78,14 @@ class WifiCallingRepository( private fun isWifiCallingSupportedFlow(): Flow { return imsMmTelRepository.imsReadyFlow().map { imsReady -> - imsReady && imsMmTelRepository.isSupported( - capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, - transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, - ) + imsReady && isWifiCallingSupported() } } + + suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) { + imsMmTelRepository.isSupported( + capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + ) + } } diff --git a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt index 824a93532b2..e79be4a9cc6 100644 --- a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt +++ b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt @@ -20,8 +20,10 @@ import android.telephony.TelephonyManager import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource +import androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import kotlinx.coroutines.Dispatchers @@ -34,6 +36,7 @@ fun AutomaticDataSwitchingPreference( ) { val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg) val coroutineScope = rememberCoroutineScope() + val crossSimCallingViewModel = viewModel() // handles backup calling SwitchPreference( object : SwitchPreferenceModel { override val title = stringResource(id = R.string.primary_sim_automatic_data_title) @@ -42,6 +45,7 @@ fun AutomaticDataSwitchingPreference( override val onCheckedChange: (Boolean) -> Unit = { newEnabled -> coroutineScope.launch(Dispatchers.Default) { setAutoDataEnabled(newEnabled) + crossSimCallingViewModel.updateCrossSimCalling() } } } @@ -54,5 +58,4 @@ fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) { policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, enabled = newEnabled, ) - //TODO: setup backup calling } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java similarity index 95% rename from tests/robotests/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragmentTest.java rename to tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java index 53e0d71c5d6..4477fa3bff2 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioDialogFragmentTest.java @@ -51,7 +51,7 @@ import java.util.ArrayList; ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class, }) -public class CallsAndAlarmsDialogFragmentTest { +public class AudioSharingCallAudioDialogFragmentTest { @Rule public final MockitoRule mocks = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -64,7 +64,7 @@ public class CallsAndAlarmsDialogFragmentTest { new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 1, /* isActive= */ true); private Fragment mParent; - private CallsAndAlarmsDialogFragment mFragment; + private AudioSharingCallAudioDialogFragment mFragment; private ShadowBluetoothAdapter mShadowBluetoothAdapter; @Before @@ -76,7 +76,7 @@ public class CallsAndAlarmsDialogFragmentTest { BluetoothStatusCodes.FEATURE_SUPPORTED); mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( BluetoothStatusCodes.FEATURE_SUPPORTED); - mFragment = new CallsAndAlarmsDialogFragment(); + mFragment = new AudioSharingCallAudioDialogFragment(); mParent = new Fragment(); FragmentController.setupFragment( mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java similarity index 98% rename from tests/robotests/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceControllerTest.java rename to tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java index 614cb5ba3b9..bdfc71fed0e 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/CallsAndAlarmsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingCallAudioPreferenceControllerTest.java @@ -88,7 +88,7 @@ import java.util.List; ShadowBluetoothUtils.class, ShadowThreadUtils.class, }) -public class CallsAndAlarmsPreferenceControllerTest { +public class AudioSharingCallAudioPreferenceControllerTest { private static final String PREF_KEY = "calls_and_alarms"; private static final String TEST_DEVICE_NAME1 = "test1"; private static final String TEST_DEVICE_NAME2 = "test2"; @@ -118,7 +118,7 @@ public class CallsAndAlarmsPreferenceControllerTest { @Mock private CachedBluetoothDevice mCachedDevice3; @Mock private BluetoothLeBroadcastReceiveState mState; @Mock private ContentResolver mContentResolver; - private CallsAndAlarmsPreferenceController mController; + private AudioSharingCallAudioPreferenceController mController; @Spy private ContentObserver mContentObserver; private ShadowBluetoothAdapter mShadowBluetoothAdapter; private LocalBluetoothManager mBtManager; @@ -151,7 +151,7 @@ public class CallsAndAlarmsPreferenceControllerTest { bisSyncState.add(1L); when(mState.getBisSyncState()).thenReturn(bisSyncState); when(mContext.getContentResolver()).thenReturn(mContentResolver); - mController = new CallsAndAlarmsPreferenceController(mContext); + mController = new AudioSharingCallAudioPreferenceController(mContext); mController.init(null); mContentObserver = mController.getSettingsObserver(); mPreference = new Preference(mContext); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java index eb72b963e0b..dc9b51d20d1 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/db/AppUsageEventEntityTest.java @@ -27,7 +27,7 @@ import org.robolectric.RobolectricTestRunner; public final class AppUsageEventEntityTest { @Test public void testBuilder_returnsExpectedResult() { - final long uid = 101L; + final int uid = 101; final long userId = 1001L; final long timestamp = 10001L; final int appUsageEventType = 1; diff --git a/tests/robotests/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceControllerTest.java index 29592cf9189..8db4681784e 100644 --- a/tests/robotests/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/telephony/AutoDataSwitchPreferenceControllerTest.java @@ -85,7 +85,7 @@ public class AutoDataSwitchPreferenceControllerTest { return true; } }; - mController.init(SUB_ID_1); + mController.init(SUB_ID_1, null); } @Test diff --git a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java index 915231f2573..10b17a7b862 100644 --- a/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java +++ b/tests/robotests/src/com/android/settings/testutils/BatteryTestUtils.java @@ -169,7 +169,7 @@ public class BatteryTestUtils { Context context, long userId, long timestamp, String packageName, boolean multiple) { final AppUsageEventEntity entity = new AppUsageEventEntity( - /* uid= */ 101L, + /* uid= */ 101, userId, timestamp, /* appUsageEventType= */ 2, diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt index b2ee018faf0..e233fa428a5 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionRepositoryTest.kt @@ -115,6 +115,27 @@ class SubscriptionRepositoryTest { .containsExactly(SIM_SLOT_INDEX_0, SIM_SLOT_INDEX_1).inOrder() } + @Test + fun getSelectableSubscriptionInfoList_oneNotInSlot_inSlotSortedFirst() { + mockSubscriptionManager.stub { + on { getAvailableSubscriptionInfoList() } doReturn listOf( + SubscriptionInfo.Builder().apply { + setSimSlotIndex(SubscriptionManager.INVALID_SIM_SLOT_INDEX) + setId(SUB_ID_3_NOT_IN_SLOT) + }.build(), + SubscriptionInfo.Builder().apply { + setSimSlotIndex(SIM_SLOT_INDEX_1) + setId(SUB_ID_IN_SLOT_1) + }.build(), + ) + } + + val subInfos = context.getSelectableSubscriptionInfoList() + + assertThat(subInfos.map { it.simSlotIndex }) + .containsExactly(SIM_SLOT_INDEX_1, SubscriptionManager.INVALID_SIM_SLOT_INDEX).inOrder() + } + @Test fun getSelectableSubscriptionInfoList_sameGroupAndOneHasSlot_returnTheOneWithSimSlotIndex() { mockSubscriptionManager.stub { diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt index f198660e5da..04cba732b73 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsMmTelRepositoryTest.kt @@ -43,6 +43,7 @@ import org.mockito.kotlin.doThrow import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.stub +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class ImsMmTelRepositoryTest { @@ -155,6 +156,13 @@ class ImsMmTelRepositoryTest { assertThat(isSupported).isTrue() } + @Test + fun setCrossSimCallingEnabled() = runBlocking { + repository.setCrossSimCallingEnabled(true) + + verify(mockImsMmTelManager).setCrossSimCallingEnabled(true) + } + private companion object { const val SUB_ID = 1 const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt index 1f3acc29afb..0144f6669cb 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt @@ -17,15 +17,18 @@ package com.android.settings.network.telephony.wificalling import android.content.Context +import android.telephony.AccessNetworkConstants import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL import android.telephony.TelephonyManager import android.telephony.ims.ImsMmTelManager +import android.telephony.ims.feature.MmTelFeature import androidx.core.os.persistableBundleOf import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any @@ -98,6 +101,22 @@ class WifiCallingRepositoryTest { assertThat(wiFiCallingMode).isEqualTo(ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED) } + @Test + fun isWifiCallingSupported() = runBlocking { + mockImsMmTelRepository.stub { + onBlocking { + isSupported( + capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + ) + } doReturn true + } + + val isSupported = repository.isWifiCallingSupported() + + assertThat(isSupported).isTrue() + } + private fun mockUseWfcHomeModeForRoaming(config: Boolean) { mockCarrierConfigManager.stub { on {