1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings; 18 19 import android.app.settings.SettingsEnums; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.Looper; 23 import android.os.UserHandle; 24 import android.provider.Settings; 25 import android.telephony.PhoneStateListener; 26 import android.telephony.SubscriptionInfo; 27 import android.telephony.TelephonyManager; 28 import android.util.Log; 29 30 import androidx.annotation.VisibleForTesting; 31 32 import com.android.internal.telephony.flags.Flags; 33 import com.android.settings.network.GlobalSettingsChangeListener; 34 import com.android.settings.network.ProxySubscriptionManager; 35 import com.android.settings.overlay.FeatureFactory; 36 import com.android.settingslib.WirelessUtils; 37 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 38 39 import java.util.List; 40 41 /** 42 * Monitor and update configuration of airplane mode settings 43 */ 44 public class AirplaneModeEnabler extends GlobalSettingsChangeListener { 45 46 private static final String LOG_TAG = "AirplaneModeEnabler"; 47 private static final boolean DEBUG = false; 48 49 private final Context mContext; 50 private final MetricsFeatureProvider mMetricsFeatureProvider; 51 52 private OnAirplaneModeChangedListener mOnAirplaneModeChangedListener; 53 54 public interface OnAirplaneModeChangedListener { 55 /** 56 * Called when airplane mode status is changed. 57 * 58 * @param isAirplaneModeOn the airplane mode is on 59 */ onAirplaneModeChanged(boolean isAirplaneModeOn)60 void onAirplaneModeChanged(boolean isAirplaneModeOn); 61 } 62 63 private TelephonyManager mTelephonyManager; 64 @VisibleForTesting 65 PhoneStateListener mPhoneStateListener; 66 AirplaneModeEnabler(Context context, OnAirplaneModeChangedListener listener)67 public AirplaneModeEnabler(Context context, OnAirplaneModeChangedListener listener) { 68 super(context, Settings.Global.AIRPLANE_MODE_ON); 69 70 mContext = context; 71 mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); 72 mOnAirplaneModeChangedListener = listener; 73 74 mTelephonyManager = context.getSystemService(TelephonyManager.class); 75 76 mPhoneStateListener = new PhoneStateListener(Looper.getMainLooper()) { 77 @Override 78 public void onRadioPowerStateChanged(int state) { 79 if (DEBUG) { 80 Log.d(LOG_TAG, "RadioPower: " + state); 81 } 82 onAirplaneModeChanged(); 83 } 84 }; 85 } 86 87 /** 88 * Implementation of GlobalSettingsChangeListener.onChanged 89 */ 90 @Override onChanged(String field)91 public void onChanged(String field) { 92 if (DEBUG) { 93 Log.d(LOG_TAG, "Airplane mode configuration update"); 94 } 95 onAirplaneModeChanged(); 96 } 97 98 /** 99 * Start listening to the phone state change 100 */ start()101 public void start() { 102 mTelephonyManager.listen(mPhoneStateListener, 103 PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED); 104 } 105 106 /** 107 * Stop listening to the phone state change 108 */ stop()109 public void stop() { 110 mTelephonyManager.listen(mPhoneStateListener, 111 PhoneStateListener.LISTEN_NONE); 112 } 113 setAirplaneModeOn(boolean enabling)114 private void setAirplaneModeOn(boolean enabling) { 115 // Change the system setting 116 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 117 enabling ? 1 : 0); 118 119 // Notify listener the system setting is changed. 120 if (mOnAirplaneModeChangedListener != null) { 121 mOnAirplaneModeChangedListener.onAirplaneModeChanged(enabling); 122 } 123 124 // Post the intent 125 final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); 126 intent.putExtra("state", enabling); 127 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 128 } 129 130 /** 131 * Called when we've received confirmation that the airplane mode was set. 132 * TODO: We update the checkbox summary when we get notified 133 * that mobile radio is powered up/down. We should not have dependency 134 * on one radio alone. We need to do the following: 135 * - handle the case of wifi/bluetooth failures 136 * - mobile does not send failure notification, fail on timeout. 137 */ onAirplaneModeChanged()138 private void onAirplaneModeChanged() { 139 if (mOnAirplaneModeChangedListener != null) { 140 mOnAirplaneModeChangedListener.onAirplaneModeChanged(isAirplaneModeOn()); 141 } 142 } 143 144 /** 145 * Check the status of ECM mode 146 * 147 * @return any subscription within device is under ECM mode 148 */ isInEcmMode()149 public boolean isInEcmMode() { 150 if (Flags.enforceTelephonyFeatureMappingForPublicApis()) { 151 try { 152 if (mTelephonyManager.getEmergencyCallbackMode()) { 153 return true; 154 } 155 } catch (UnsupportedOperationException e) { 156 // Device doesn't support FEATURE_TELEPHONY_CALLING 157 // Ignore exception, device is not in ECM mode. 158 } 159 } else { 160 if (mTelephonyManager.getEmergencyCallbackMode()) { 161 return true; 162 } 163 } 164 final List<SubscriptionInfo> subInfoList = 165 ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionsInfo(); 166 if (subInfoList == null) { 167 return false; 168 } 169 for (SubscriptionInfo subInfo : subInfoList) { 170 final TelephonyManager telephonyManager = 171 mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId()); 172 if (telephonyManager != null) { 173 if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) { 174 if (telephonyManager.getEmergencyCallbackMode()) { 175 return true; 176 } 177 } else { 178 try { 179 if (telephonyManager.getEmergencyCallbackMode()) { 180 return true; 181 } 182 } catch (UnsupportedOperationException e) { 183 // Ignore exception, device is not in ECM mode. 184 } 185 } 186 } 187 } 188 return false; 189 } 190 setAirplaneMode(boolean isAirplaneModeOn)191 public void setAirplaneMode(boolean isAirplaneModeOn) { 192 if (isInEcmMode()) { 193 // In ECM mode, do not update database at this point 194 Log.d(LOG_TAG, "ECM airplane mode=" + isAirplaneModeOn); 195 } else { 196 mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AIRPLANE_TOGGLE, 197 isAirplaneModeOn); 198 setAirplaneModeOn(isAirplaneModeOn); 199 } 200 } 201 setAirplaneModeInECM(boolean isECMExit, boolean isAirplaneModeOn)202 public void setAirplaneModeInECM(boolean isECMExit, boolean isAirplaneModeOn) { 203 Log.d(LOG_TAG, "Exist ECM=" + isECMExit + ", with airplane mode=" + isAirplaneModeOn); 204 if (isECMExit) { 205 // update database based on the current checkbox state 206 setAirplaneModeOn(isAirplaneModeOn); 207 } else { 208 // update summary 209 onAirplaneModeChanged(); 210 } 211 } 212 isAirplaneModeOn()213 public boolean isAirplaneModeOn() { 214 return WirelessUtils.isAirplaneModeOn(mContext); 215 } 216 } 217 218