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