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.UserHandle;
23 import android.provider.Settings;
24 import android.telephony.PhoneStateListener;
25 import android.telephony.SubscriptionInfo;
26 import android.telephony.TelephonyManager;
27 import android.util.Log;
28 
29 import androidx.annotation.VisibleForTesting;
30 
31 import com.android.settings.network.GlobalSettingsChangeListener;
32 import com.android.settings.network.ProxySubscriptionManager;
33 import com.android.settings.overlay.FeatureFactory;
34 import com.android.settingslib.WirelessUtils;
35 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
36 
37 import java.util.List;
38 
39 /**
40  * Monitor and update configuration of airplane mode settings
41  */
42 public class AirplaneModeEnabler extends GlobalSettingsChangeListener {
43 
44     private static final String LOG_TAG = "AirplaneModeEnabler";
45     private static final boolean DEBUG = false;
46 
47     private final Context mContext;
48     private final MetricsFeatureProvider mMetricsFeatureProvider;
49 
50     private OnAirplaneModeChangedListener mOnAirplaneModeChangedListener;
51 
52     public interface OnAirplaneModeChangedListener {
53         /**
54          * Called when airplane mode status is changed.
55          *
56          * @param isAirplaneModeOn the airplane mode is on
57          */
onAirplaneModeChanged(boolean isAirplaneModeOn)58         void onAirplaneModeChanged(boolean isAirplaneModeOn);
59     }
60 
61     private TelephonyManager mTelephonyManager;
62     @VisibleForTesting
63     PhoneStateListener mPhoneStateListener;
64 
65     private GlobalSettingsChangeListener mAirplaneModeObserver;
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.getFactory(context).getMetricsFeatureProvider();
72         mOnAirplaneModeChangedListener = listener;
73 
74         mTelephonyManager = context.getSystemService(TelephonyManager.class);
75 
76         mPhoneStateListener = new PhoneStateListener() {
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      */
onChanged(String field)90     public void onChanged(String field) {
91         if (DEBUG) {
92             Log.d(LOG_TAG, "Airplane mode configuration update");
93         }
94         onAirplaneModeChanged();
95     }
96 
resume()97     public void resume() {
98         mTelephonyManager.listen(mPhoneStateListener,
99                 PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED);
100     }
101 
pause()102     public void pause() {
103         mTelephonyManager.listen(mPhoneStateListener,
104                 PhoneStateListener.LISTEN_NONE);
105     }
106 
setAirplaneModeOn(boolean enabling)107     private void setAirplaneModeOn(boolean enabling) {
108         // Change the system setting
109         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
110                 enabling ? 1 : 0);
111 
112         // Notify listener the system setting is changed.
113         if (mOnAirplaneModeChangedListener != null) {
114             mOnAirplaneModeChangedListener.onAirplaneModeChanged(enabling);
115         }
116 
117         // Post the intent
118         final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
119         intent.putExtra("state", enabling);
120         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
121     }
122 
123     /**
124      * Called when we've received confirmation that the airplane mode was set.
125      * TODO: We update the checkbox summary when we get notified
126      * that mobile radio is powered up/down. We should not have dependency
127      * on one radio alone. We need to do the following:
128      * - handle the case of wifi/bluetooth failures
129      * - mobile does not send failure notification, fail on timeout.
130      */
onAirplaneModeChanged()131     private void onAirplaneModeChanged() {
132         if (mOnAirplaneModeChangedListener != null) {
133             mOnAirplaneModeChangedListener.onAirplaneModeChanged(isAirplaneModeOn());
134         }
135     }
136 
137     /**
138      * Check the status of ECM mode
139      *
140      * @return any subscription within device is under ECM mode
141      */
isInEcmMode()142     public boolean isInEcmMode() {
143         if (mTelephonyManager.getEmergencyCallbackMode()) {
144             return true;
145         }
146         final List<SubscriptionInfo> subInfoList =
147                 ProxySubscriptionManager.getInstance(mContext).getActiveSubscriptionsInfo();
148         if (subInfoList == null) {
149             return false;
150         }
151         for (SubscriptionInfo subInfo : subInfoList) {
152             final TelephonyManager telephonyManager =
153                     mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
154             if (telephonyManager != null) {
155                 if (telephonyManager.getEmergencyCallbackMode()) {
156                     return true;
157                 }
158             }
159         }
160         return false;
161     }
162 
setAirplaneMode(boolean isAirplaneModeOn)163     public void setAirplaneMode(boolean isAirplaneModeOn) {
164         if (isInEcmMode()) {
165             // In ECM mode, do not update database at this point
166             Log.d(LOG_TAG, "ECM airplane mode=" + isAirplaneModeOn);
167         } else {
168             mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_AIRPLANE_TOGGLE,
169                     isAirplaneModeOn);
170             setAirplaneModeOn(isAirplaneModeOn);
171         }
172     }
173 
setAirplaneModeInECM(boolean isECMExit, boolean isAirplaneModeOn)174     public void setAirplaneModeInECM(boolean isECMExit, boolean isAirplaneModeOn) {
175         Log.d(LOG_TAG, "Exist ECM=" + isECMExit + ", with airplane mode=" + isAirplaneModeOn);
176         if (isECMExit) {
177             // update database based on the current checkbox state
178             setAirplaneModeOn(isAirplaneModeOn);
179         } else {
180             // update summary
181             onAirplaneModeChanged();
182         }
183     }
184 
isAirplaneModeOn()185     public boolean isAirplaneModeOn() {
186         return WirelessUtils.isAirplaneModeOn(mContext);
187     }
188 }
189