Loading src/com/android/settings/AirplaneModeEnabler.java +23 −8 Original line number Diff line number Diff line Loading @@ -147,9 +147,24 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { * @return any subscription within device is under ECM mode */ public boolean isInEcmMode() { return isInEcmMode(mContext, mTelephonyManager); } /** * Check the status of ECM mode * * @param context Caller's {@link Context} * @param telephonyManager The default {@link TelephonyManager} * * @return any subscription within device is under ECM mode */ public static boolean isInEcmMode(Context context, TelephonyManager telephonyManager) { if (context == null || telephonyManager == null) { return false; } if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { try { if (mTelephonyManager.getEmergencyCallbackMode()) { if (telephonyManager.getEmergencyCallbackMode()) { return true; } } catch (UnsupportedOperationException e) { Loading @@ -157,26 +172,26 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { // Ignore exception, device is not in ECM mode. } } else { if (mTelephonyManager.getEmergencyCallbackMode()) { if (telephonyManager.getEmergencyCallbackMode()) { return true; } } final List<SubscriptionInfo> subInfoList = ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionsInfo(); ProxySubscriptionManager.getInstance(context).getActiveSubscriptionsInfo(); if (subInfoList == null) { return false; } for (SubscriptionInfo subInfo : subInfoList) { final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); if (telephonyManager != null) { final TelephonyManager telephonyManagerForSubId = telephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); if (telephonyManagerForSubId != null) { if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) { if (telephonyManager.getEmergencyCallbackMode()) { if (telephonyManagerForSubId.getEmergencyCallbackMode()) { return true; } } else { try { if (telephonyManager.getEmergencyCallbackMode()) { if (telephonyManagerForSubId.getEmergencyCallbackMode()) { return true; } } catch (UnsupportedOperationException e) { Loading src/com/android/settings/network/AirplaneModePreference.kt +160 −6 Original line number Diff line number Diff line Loading @@ -16,34 +16,188 @@ package com.android.settings.network import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Looper import android.os.UserHandle import android.os.UserManager import android.provider.Settings import android.telephony.PhoneStateListener import android.telephony.TelephonyManager import android.util.Log import androidx.annotation.DrawableRes import androidx.preference.Preference import com.android.settings.AirplaneModeEnabler import com.android.settings.PreferenceRestrictionMixin import com.android.settings.R import com.android.settings.Utils import com.android.settingslib.RestrictedSwitchPreference import com.android.settingslib.datastore.AbstractKeyedDataObservable import com.android.settingslib.datastore.DataChangeReason import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.SettingsGlobalStore import com.android.settingslib.datastore.SettingsStore import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.metadata.SwitchPreference import com.android.settingslib.preference.SwitchPreferenceBinding import java.util.concurrent.Executors import java.util.concurrent.TimeUnit // LINT.IfChange class AirplaneModePreference : SwitchPreference(KEY, R.string.airplane_mode), PreferenceAvailabilityProvider { open class AirplaneModePreference : SwitchPreference(KEY, R.string.airplane_mode), SwitchPreferenceBinding, PreferenceAvailabilityProvider, PreferenceLifecycleProvider, PreferenceRestrictionMixin { override val icon: Int @DrawableRes get() = R.drawable.ic_airplanemode_active override fun storage(context: Context) = SettingsGlobalStore.get(context) override fun isAvailable(context: Context) = (context.resources.getBoolean(R.bool.config_show_toggle_airplane) && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context) override val restrictionKeys get() = arrayOf(UserManager.DISALLOW_AIRPLANE_MODE) override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) = ReadWritePermit.ALLOW override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = when { isSatelliteOn(context) || isInEcmMode(context) -> ReadWritePermit.DISALLOW else -> ReadWritePermit.ALLOW } override val sensitivityLevel get() = SensitivityLevel.HIGH_SENSITIVITY override fun isAvailable(context: Context) = (context.resources.getBoolean(R.bool.config_show_toggle_airplane) && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) override fun storage(context: Context): KeyValueStore = AirplaneModeStorage(context, SettingsGlobalStore.get(context)) @Suppress("DEPRECATION", "MissingPermission", "UNCHECKED_CAST") private class AirplaneModeStorage( private val context: Context, private val settingsStore: SettingsStore, ) : AbstractKeyedDataObservable<String>(), KeyValueStore { private var phoneStateListener: PhoneStateListener? = null override fun contains(key: String) = settingsStore.contains(KEY) && context.getSystemService(TelephonyManager::class.java) != null override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) = DEFAULT_VALUE as T override fun <T : Any> getValue(key: String, valueType: Class<T>): T = (settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) { if (value is Boolean) { settingsStore.setBoolean(key, value) val intent = Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) intent.putExtra("state", value) context.sendBroadcastAsUser(intent, UserHandle.ALL) } } override fun onFirstObserverAdded() { context.getSystemService(TelephonyManager::class.java)?.let { phoneStateListener = object : PhoneStateListener(Looper.getMainLooper()) { @Deprecated("Deprecated in Java") override fun onRadioPowerStateChanged(state: Int) { Log.d(TAG, "onRadioPowerStateChanged(), state=$state") notifyChange(KEY, DataChangeReason.UPDATE) } } it.listen(phoneStateListener, PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) } } override fun onLastObserverRemoved() { context .getSystemService(TelephonyManager::class.java) ?.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) } } override fun onCreate(context: PreferenceLifecycleContext) { context.requirePreference<RestrictedSwitchPreference>(KEY).onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _: Preference, _: Any -> if (isInEcmMode(context)) { showEcmDialog(context) return@OnPreferenceChangeListener false } if (isSatelliteOn(context)) { showSatelliteDialog(context) return@OnPreferenceChangeListener false } return@OnPreferenceChangeListener true } } override fun onActivityResult( context: PreferenceLifecycleContext, requestCode: Int, resultCode: Int, data: Intent?, ): Boolean { if (requestCode == REQUEST_CODE_EXIT_ECM && resultCode == Activity.RESULT_OK) { storage(context).setValue(KEY, Boolean::class.javaObjectType, true) } return true } private fun isInEcmMode(context: Context) = AirplaneModeEnabler.isInEcmMode( context, context.getSystemService(TelephonyManager::class.java), ) private fun isSatelliteOn(context: Context): Boolean { try { return SatelliteRepository(context) .requestIsSessionStarted(Executors.newSingleThreadExecutor()) .get(2000, TimeUnit.MILLISECONDS) } catch (e: Exception) { Log.e(TAG, "Error to get satellite status : $e") } return false } private fun showEcmDialog(context: PreferenceLifecycleContext) { val intent = Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null) .setPackage(Utils.PHONE_PACKAGE_NAME) context.startActivityForResult(intent, REQUEST_CODE_EXIT_ECM, null) } private fun showSatelliteDialog(context: PreferenceLifecycleContext) { val intent = Intent(context, SatelliteWarningDialogActivity::class.java) .putExtra( SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, SatelliteWarningDialogActivity.TYPE_IS_AIRPLANE_MODE, ) context.startActivity(intent) } companion object { const val TAG = "AirplaneModePreference" const val KEY = Settings.Global.AIRPLANE_MODE_ON const val DEFAULT_VALUE = false const val REQUEST_CODE_EXIT_ECM = 1 fun Context.isAirplaneModeOn() = SettingsGlobalStore.get(this).getBoolean(KEY) == true } Loading src/com/android/settings/network/NetworkDashboardFragment.java +7 −3 Original line number Diff line number Diff line Loading @@ -59,7 +59,9 @@ public class NetworkDashboardFragment extends DashboardFragment implements public void onAttach(Context context) { super.onAttach(context); if (isCatalystEnabled()) { use(AirplaneModePreferenceController.class).setFragment(this); } use(NetworkProviderCallsSmsController.class).init(this); } Loading Loading @@ -102,8 +104,10 @@ public class NetworkDashboardFragment extends DashboardFragment implements switch (requestCode) { case AirplaneModePreferenceController.REQUEST_CODE_EXIT_ECM: if (isCatalystEnabled()) { use(AirplaneModePreferenceController.class) .onActivityResult(requestCode, resultCode, data); } break; } } Loading src/com/android/settings/network/NetworkDashboardScreen.kt +1 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ class NetworkDashboardScreen : PreferenceScreenCreator, PreferenceIconProvider { override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { +MobileNetworkListScreen.KEY order -15 +AirplaneModePreference() order -5 +DataSaverScreen.KEY order 10 } Loading tests/unit/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java +9 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.settings.network; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.settings.flags.Flags.FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -28,6 +32,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.os.Looper; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.util.AndroidRuntimeException; Loading @@ -42,6 +47,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; Loading @@ -49,6 +55,8 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class AirplaneModePreferenceControllerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); private static final int ON = 1; private static final int OFF = 0; Loading @@ -66,6 +74,7 @@ public class AirplaneModePreferenceControllerTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN); if (Looper.myLooper() == null) { Looper.prepare(); } Loading Loading
src/com/android/settings/AirplaneModeEnabler.java +23 −8 Original line number Diff line number Diff line Loading @@ -147,9 +147,24 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { * @return any subscription within device is under ECM mode */ public boolean isInEcmMode() { return isInEcmMode(mContext, mTelephonyManager); } /** * Check the status of ECM mode * * @param context Caller's {@link Context} * @param telephonyManager The default {@link TelephonyManager} * * @return any subscription within device is under ECM mode */ public static boolean isInEcmMode(Context context, TelephonyManager telephonyManager) { if (context == null || telephonyManager == null) { return false; } if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { try { if (mTelephonyManager.getEmergencyCallbackMode()) { if (telephonyManager.getEmergencyCallbackMode()) { return true; } } catch (UnsupportedOperationException e) { Loading @@ -157,26 +172,26 @@ public class AirplaneModeEnabler extends GlobalSettingsChangeListener { // Ignore exception, device is not in ECM mode. } } else { if (mTelephonyManager.getEmergencyCallbackMode()) { if (telephonyManager.getEmergencyCallbackMode()) { return true; } } final List<SubscriptionInfo> subInfoList = ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionsInfo(); ProxySubscriptionManager.getInstance(context).getActiveSubscriptionsInfo(); if (subInfoList == null) { return false; } for (SubscriptionInfo subInfo : subInfoList) { final TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); if (telephonyManager != null) { final TelephonyManager telephonyManagerForSubId = telephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); if (telephonyManagerForSubId != null) { if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) { if (telephonyManager.getEmergencyCallbackMode()) { if (telephonyManagerForSubId.getEmergencyCallbackMode()) { return true; } } else { try { if (telephonyManager.getEmergencyCallbackMode()) { if (telephonyManagerForSubId.getEmergencyCallbackMode()) { return true; } } catch (UnsupportedOperationException e) { Loading
src/com/android/settings/network/AirplaneModePreference.kt +160 −6 Original line number Diff line number Diff line Loading @@ -16,34 +16,188 @@ package com.android.settings.network import android.app.Activity import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.Looper import android.os.UserHandle import android.os.UserManager import android.provider.Settings import android.telephony.PhoneStateListener import android.telephony.TelephonyManager import android.util.Log import androidx.annotation.DrawableRes import androidx.preference.Preference import com.android.settings.AirplaneModeEnabler import com.android.settings.PreferenceRestrictionMixin import com.android.settings.R import com.android.settings.Utils import com.android.settingslib.RestrictedSwitchPreference import com.android.settingslib.datastore.AbstractKeyedDataObservable import com.android.settingslib.datastore.DataChangeReason import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.SettingsGlobalStore import com.android.settingslib.datastore.SettingsStore import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceLifecycleContext import com.android.settingslib.metadata.PreferenceLifecycleProvider import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.metadata.SwitchPreference import com.android.settingslib.preference.SwitchPreferenceBinding import java.util.concurrent.Executors import java.util.concurrent.TimeUnit // LINT.IfChange class AirplaneModePreference : SwitchPreference(KEY, R.string.airplane_mode), PreferenceAvailabilityProvider { open class AirplaneModePreference : SwitchPreference(KEY, R.string.airplane_mode), SwitchPreferenceBinding, PreferenceAvailabilityProvider, PreferenceLifecycleProvider, PreferenceRestrictionMixin { override val icon: Int @DrawableRes get() = R.drawable.ic_airplanemode_active override fun storage(context: Context) = SettingsGlobalStore.get(context) override fun isAvailable(context: Context) = (context.resources.getBoolean(R.bool.config_show_toggle_airplane) && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context) override val restrictionKeys get() = arrayOf(UserManager.DISALLOW_AIRPLANE_MODE) override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) = ReadWritePermit.ALLOW override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) = when { isSatelliteOn(context) || isInEcmMode(context) -> ReadWritePermit.DISALLOW else -> ReadWritePermit.ALLOW } override val sensitivityLevel get() = SensitivityLevel.HIGH_SENSITIVITY override fun isAvailable(context: Context) = (context.resources.getBoolean(R.bool.config_show_toggle_airplane) && !context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) override fun storage(context: Context): KeyValueStore = AirplaneModeStorage(context, SettingsGlobalStore.get(context)) @Suppress("DEPRECATION", "MissingPermission", "UNCHECKED_CAST") private class AirplaneModeStorage( private val context: Context, private val settingsStore: SettingsStore, ) : AbstractKeyedDataObservable<String>(), KeyValueStore { private var phoneStateListener: PhoneStateListener? = null override fun contains(key: String) = settingsStore.contains(KEY) && context.getSystemService(TelephonyManager::class.java) != null override fun <T : Any> getDefaultValue(key: String, valueType: Class<T>) = DEFAULT_VALUE as T override fun <T : Any> getValue(key: String, valueType: Class<T>): T = (settingsStore.getBoolean(key) ?: DEFAULT_VALUE) as T override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) { if (value is Boolean) { settingsStore.setBoolean(key, value) val intent = Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) intent.putExtra("state", value) context.sendBroadcastAsUser(intent, UserHandle.ALL) } } override fun onFirstObserverAdded() { context.getSystemService(TelephonyManager::class.java)?.let { phoneStateListener = object : PhoneStateListener(Looper.getMainLooper()) { @Deprecated("Deprecated in Java") override fun onRadioPowerStateChanged(state: Int) { Log.d(TAG, "onRadioPowerStateChanged(), state=$state") notifyChange(KEY, DataChangeReason.UPDATE) } } it.listen(phoneStateListener, PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) } } override fun onLastObserverRemoved() { context .getSystemService(TelephonyManager::class.java) ?.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) } } override fun onCreate(context: PreferenceLifecycleContext) { context.requirePreference<RestrictedSwitchPreference>(KEY).onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _: Preference, _: Any -> if (isInEcmMode(context)) { showEcmDialog(context) return@OnPreferenceChangeListener false } if (isSatelliteOn(context)) { showSatelliteDialog(context) return@OnPreferenceChangeListener false } return@OnPreferenceChangeListener true } } override fun onActivityResult( context: PreferenceLifecycleContext, requestCode: Int, resultCode: Int, data: Intent?, ): Boolean { if (requestCode == REQUEST_CODE_EXIT_ECM && resultCode == Activity.RESULT_OK) { storage(context).setValue(KEY, Boolean::class.javaObjectType, true) } return true } private fun isInEcmMode(context: Context) = AirplaneModeEnabler.isInEcmMode( context, context.getSystemService(TelephonyManager::class.java), ) private fun isSatelliteOn(context: Context): Boolean { try { return SatelliteRepository(context) .requestIsSessionStarted(Executors.newSingleThreadExecutor()) .get(2000, TimeUnit.MILLISECONDS) } catch (e: Exception) { Log.e(TAG, "Error to get satellite status : $e") } return false } private fun showEcmDialog(context: PreferenceLifecycleContext) { val intent = Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null) .setPackage(Utils.PHONE_PACKAGE_NAME) context.startActivityForResult(intent, REQUEST_CODE_EXIT_ECM, null) } private fun showSatelliteDialog(context: PreferenceLifecycleContext) { val intent = Intent(context, SatelliteWarningDialogActivity::class.java) .putExtra( SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, SatelliteWarningDialogActivity.TYPE_IS_AIRPLANE_MODE, ) context.startActivity(intent) } companion object { const val TAG = "AirplaneModePreference" const val KEY = Settings.Global.AIRPLANE_MODE_ON const val DEFAULT_VALUE = false const val REQUEST_CODE_EXIT_ECM = 1 fun Context.isAirplaneModeOn() = SettingsGlobalStore.get(this).getBoolean(KEY) == true } Loading
src/com/android/settings/network/NetworkDashboardFragment.java +7 −3 Original line number Diff line number Diff line Loading @@ -59,7 +59,9 @@ public class NetworkDashboardFragment extends DashboardFragment implements public void onAttach(Context context) { super.onAttach(context); if (isCatalystEnabled()) { use(AirplaneModePreferenceController.class).setFragment(this); } use(NetworkProviderCallsSmsController.class).init(this); } Loading Loading @@ -102,8 +104,10 @@ public class NetworkDashboardFragment extends DashboardFragment implements switch (requestCode) { case AirplaneModePreferenceController.REQUEST_CODE_EXIT_ECM: if (isCatalystEnabled()) { use(AirplaneModePreferenceController.class) .onActivityResult(requestCode, resultCode, data); } break; } } Loading
src/com/android/settings/network/NetworkDashboardScreen.kt +1 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ class NetworkDashboardScreen : PreferenceScreenCreator, PreferenceIconProvider { override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(this) { +MobileNetworkListScreen.KEY order -15 +AirplaneModePreference() order -5 +DataSaverScreen.KEY order 10 } Loading
tests/unit/src/com/android/settings/network/AirplaneModePreferenceControllerTest.java +9 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,10 @@ package com.android.settings.network; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.settings.flags.Flags.FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; Loading @@ -28,6 +32,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.os.Looper; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.SettingsSlicesContract; import android.util.AndroidRuntimeException; Loading @@ -42,6 +47,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settingslib.RestrictedSwitchPreference; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; Loading @@ -49,6 +55,8 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class AirplaneModePreferenceControllerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); private static final int ON = 1; private static final int OFF = 0; Loading @@ -66,6 +74,7 @@ public class AirplaneModePreferenceControllerTest { @Before public void setUp() { mSetFlagsRule.disableFlags(FLAG_CATALYST_NETWORK_PROVIDER_AND_INTERNET_SCREEN); if (Looper.myLooper() == null) { Looper.prepare(); } Loading