• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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.server.wifi;
18 
19 import android.app.ActivityManager;
20 import android.app.Notification;
21 import android.app.PendingIntent;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.graphics.drawable.Icon;
26 import android.net.Uri;
27 import android.net.wifi.WifiContext;
28 import android.provider.Settings;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
32 import com.android.wifi.resources.R;
33 
34 import java.io.FileDescriptor;
35 import java.io.PrintWriter;
36 
37 /* Tracks persisted settings for Wi-Fi and airplane mode interaction */
38 public class WifiSettingsStore {
39     /* Values used to track the current state of Wi-Fi
40      * Key: Settings.Global.WIFI_ON
41      * Values:
42      *     WIFI_DISABLED
43      *     WIFI_ENABLED
44      *     WIFI_ENABLED_APM_OVERRIDE
45      *     WIFI_DISABLED_APM_ON
46      */
47     @VisibleForTesting
48     public static final int WIFI_DISABLED                      = 0;
49     @VisibleForTesting
50     public static final int WIFI_ENABLED                       = 1;
51     /* Wifi enabled while in airplane mode */
52     @VisibleForTesting
53     public static final int WIFI_ENABLED_APM_OVERRIDE = 2;
54     /* Wifi disabled due to airplane mode on */
55     @VisibleForTesting
56     public static final int WIFI_DISABLED_APM_ON = 3;
57 
58     /* Values used to track the current state of airplane mode
59      * Key: Settings.Global.AIRPLANE_MODE_ON
60      * Values:
61      *     APM_DISABLED
62      *     APM_ENABLED
63      */
64     @VisibleForTesting
65     public static final int APM_DISABLED                      = 0;
66     @VisibleForTesting
67     public static final int APM_ENABLED                       = 1;
68 
69     /* Values used to track whether Wi-Fi should remain on in airplane mode
70      * Key: Settings.Secure WIFI_APM_STATE
71      * Values:
72      *     WIFI_TURNS_OFF_IN_APM
73      *     WIFI_REMAINS_ON_IN_APM
74      */
75     @VisibleForTesting
76     public static final String WIFI_APM_STATE = "wifi_apm_state";
77     @VisibleForTesting
78     public static final int WIFI_TURNS_OFF_IN_APM = 0;
79     @VisibleForTesting
80     public static final int WIFI_REMAINS_ON_IN_APM = 1;
81 
82     /* Values used to track whether Bluetooth should remain on in airplane mode
83      * Key: Settings.Secure BLUETOOTH_APM_STATE
84      * Values:
85      *     BT_TURNS_OFF_IN_APM
86      *     BT_REMAINS_ON_IN_APM
87      */
88     @VisibleForTesting
89     public static final String BLUETOOTH_APM_STATE = "bluetooth_apm_state";
90     @VisibleForTesting
91     public static final int BT_TURNS_OFF_IN_APM = 0;
92     @VisibleForTesting
93     public static final int BT_REMAINS_ON_IN_APM = 1;
94 
95     /* Values used to track whether a notification has been shown
96      * Keys:
97      *     Settings.Secure APM_WIFI_NOTIFICATION
98      *     Settings.Secure APM_WIFI_ENABLED_NOTIFICATION
99      * Values:
100      *     NOTIFICATION_NOT_SHOWN
101      *     NOTIFICATION_SHOWN
102      */
103     /* Track whether Wi-Fi remains on in airplane mode notification was shown */
104     @VisibleForTesting
105     public static final String APM_WIFI_NOTIFICATION = "apm_wifi_notification";
106     /* Track whether Wi-Fi enabled in airplane mode notification was shown */
107     @VisibleForTesting
108     public static final String APM_WIFI_ENABLED_NOTIFICATION = "apm_wifi_enabled_notification";
109     @VisibleForTesting
110     public static final int NOTIFICATION_NOT_SHOWN = 0;
111     @VisibleForTesting
112     public static final int NOTIFICATION_SHOWN = 1;
113 
114     /**
115      * @hide constant copied from {@link Settings.Global}
116      * TODO(b/274636414): Migrate to official API in Android V.
117      */
118     static final String SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios";
119     /**
120      * @hide constant copied from {@link Settings.Global}
121      * TODO(b/274636414): Migrate to official API in Android V.
122      */
123     static final String SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled";
124 
125     /* Persisted state that tracks the wifi & airplane interaction from settings */
126     private int mPersistWifiState = WIFI_DISABLED;
127 
128     /* Tracks current airplane mode state */
129     private boolean mAirplaneModeOn = false;
130 
131     /* Tracks the wifi state before entering airplane mode*/
132     private boolean mIsWifiOnBeforeEnteringApm = false;
133 
134     /* Tracks the wifi state after entering airplane mode*/
135     private boolean mIsWifiOnAfterEnteringApm = false;
136 
137     /* Tracks whether user toggled wifi in airplane mode */
138     private boolean mUserToggledWifiDuringApm = false;
139 
140     /* Tracks whether user toggled wifi within one minute of entering airplane mode */
141     private boolean mUserToggledWifiAfterEnteringApmWithinMinute = false;
142 
143     /* Tracks when airplane mode has been enabled in milliseconds since boot */
144     private long mApmEnabledTimeSinceBootMillis = 0;
145 
146     private final String mApmEnhancementHelpLink;
147     private final WifiContext mContext;
148     private final WifiSettingsConfigStore mSettingsConfigStore;
149     private final WifiThreadRunner mWifiThreadRunner;
150     private final FrameworkFacade mFrameworkFacade;
151     private final WifiNotificationManager mNotificationManager;
152     private final DeviceConfigFacade mDeviceConfigFacade;
153     private final WifiMetrics mWifiMetrics;
154     private final Clock mClock;
155     private boolean mSatelliteModeOn;
156 
WifiSettingsStore(WifiContext context, WifiSettingsConfigStore sharedPreferences, WifiThreadRunner wifiThread, FrameworkFacade frameworkFacade, WifiNotificationManager notificationManager, DeviceConfigFacade deviceConfigFacade, WifiMetrics wifiMetrics, Clock clock)157     WifiSettingsStore(WifiContext context, WifiSettingsConfigStore sharedPreferences,
158             WifiThreadRunner wifiThread, FrameworkFacade frameworkFacade,
159             WifiNotificationManager notificationManager, DeviceConfigFacade deviceConfigFacade,
160             WifiMetrics wifiMetrics, Clock clock) {
161         mContext = context;
162         mSettingsConfigStore = sharedPreferences;
163         mWifiThreadRunner = wifiThread;
164         mFrameworkFacade = frameworkFacade;
165         mNotificationManager = notificationManager;
166         mDeviceConfigFacade = deviceConfigFacade;
167         mWifiMetrics = wifiMetrics;
168         mClock = clock;
169         mAirplaneModeOn = getPersistedAirplaneModeOn();
170         mPersistWifiState = getPersistedWifiState();
171         mApmEnhancementHelpLink = mContext.getString(R.string.config_wifiApmEnhancementHelpLink);
172         mSatelliteModeOn = getPersistedSatelliteModeOn();
173     }
174 
getUserSecureIntegerSetting(String name, int def)175     private int getUserSecureIntegerSetting(String name, int def) {
176         Context userContext = mFrameworkFacade.getUserContext(mContext);
177         return mFrameworkFacade.getSecureIntegerSetting(userContext, name, def);
178     }
179 
setUserSecureIntegerSetting(String name, int value)180     private void setUserSecureIntegerSetting(String name, int value) {
181         Context userContext = mFrameworkFacade.getUserContext(mContext);
182         mFrameworkFacade.setSecureIntegerSetting(userContext, name, value);
183     }
184 
isWifiToggleEnabled()185     public synchronized boolean isWifiToggleEnabled() {
186         return mPersistWifiState == WIFI_ENABLED
187                 || mPersistWifiState == WIFI_ENABLED_APM_OVERRIDE;
188     }
189 
190     /**
191      * Returns true if airplane mode is currently on.
192      * @return {@code true} if airplane mode is on.
193      */
isAirplaneModeOn()194     public synchronized boolean isAirplaneModeOn() {
195         return mAirplaneModeOn;
196     }
197 
isScanAlwaysAvailableToggleEnabled()198     public synchronized boolean isScanAlwaysAvailableToggleEnabled() {
199         return getPersistedScanAlwaysAvailable();
200     }
201 
isScanAlwaysAvailable()202     public synchronized boolean isScanAlwaysAvailable() {
203         return !mAirplaneModeOn && getPersistedScanAlwaysAvailable();
204     }
205 
isWifiScoringEnabled()206     public synchronized boolean isWifiScoringEnabled() {
207         return getPersistedWifiScoringEnabled();
208     }
209 
isWifiPasspointEnabled()210     public synchronized boolean isWifiPasspointEnabled() {
211         return getPersistedWifiPasspointEnabled();
212     }
213 
getWifiMultiInternetMode()214     public synchronized int getWifiMultiInternetMode() {
215         return getPersistedWifiMultiInternetMode();
216     }
217 
setPersistWifiState(int state)218     public void setPersistWifiState(int state) {
219         mPersistWifiState = state;
220     }
221 
showNotification(int titleId, int messageId)222     private void showNotification(int titleId, int messageId) {
223         String settingsPackage = mFrameworkFacade.getSettingsPackageName(mContext);
224         if (settingsPackage == null) return;
225 
226         Intent openLinkIntent = new Intent(Intent.ACTION_VIEW)
227                 .setData(Uri.parse(mApmEnhancementHelpLink))
228                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
229         PendingIntent tapPendingIntent = mFrameworkFacade.getActivity(mContext, 0, openLinkIntent,
230                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
231 
232         String title = mContext.getResources().getString(titleId);
233         String message = mContext.getResources().getString(messageId);
234         Notification.Builder builder = mFrameworkFacade.makeNotificationBuilder(mContext,
235                         WifiService.NOTIFICATION_APM_ALERTS)
236                 .setAutoCancel(true)
237                 .setLocalOnly(true)
238                 .setContentTitle(title)
239                 .setContentText(message)
240                 .setContentIntent(tapPendingIntent)
241                 .setVisibility(Notification.VISIBILITY_PUBLIC)
242                 .setStyle(new Notification.BigTextStyle().bigText(message))
243                 .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
244                         R.drawable.ic_wifi_settings));
245         mNotificationManager.notify(SystemMessage.NOTE_WIFI_APM_NOTIFICATION, builder.build());
246     }
247 
handleWifiToggled(boolean wifiEnabled)248     public synchronized boolean handleWifiToggled(boolean wifiEnabled) {
249         // Can Wi-Fi be toggled in airplane mode ?
250         if (mAirplaneModeOn && !isAirplaneToggleable()) {
251             return false;
252         }
253         if (wifiEnabled) {
254             if (mAirplaneModeOn) {
255                 persistWifiState(WIFI_ENABLED_APM_OVERRIDE);
256                 if (mDeviceConfigFacade.isApmEnhancementEnabled()) {
257                     setUserSecureIntegerSetting(WIFI_APM_STATE, WIFI_REMAINS_ON_IN_APM);
258                     if (getUserSecureIntegerSetting(APM_WIFI_ENABLED_NOTIFICATION,
259                             NOTIFICATION_NOT_SHOWN) == NOTIFICATION_NOT_SHOWN) {
260                         mWifiThreadRunner.post(
261                                 () -> showNotification(R.string.wifi_enabled_apm_first_time_title,
262                                         R.string.wifi_enabled_apm_first_time_message));
263                         setUserSecureIntegerSetting(
264                                 APM_WIFI_ENABLED_NOTIFICATION, NOTIFICATION_SHOWN);
265                     }
266                 }
267             } else {
268                 persistWifiState(WIFI_ENABLED);
269             }
270         } else {
271             // When wifi state is disabled, we do not care
272             // if airplane mode is on or not. The scenario of
273             // wifi being disabled due to airplane mode being turned on
274             // is handled handleAirplaneModeToggled()
275             persistWifiState(WIFI_DISABLED);
276             if (mDeviceConfigFacade.isApmEnhancementEnabled() && mAirplaneModeOn) {
277                 setUserSecureIntegerSetting(WIFI_APM_STATE, WIFI_TURNS_OFF_IN_APM);
278             }
279         }
280         if (mAirplaneModeOn) {
281             if (!mUserToggledWifiDuringApm) {
282                 mUserToggledWifiAfterEnteringApmWithinMinute =
283                         mClock.getElapsedSinceBootMillis() - mApmEnabledTimeSinceBootMillis
284                                 < 60_000;
285             }
286             mUserToggledWifiDuringApm = true;
287         }
288         return true;
289     }
290 
291     synchronized boolean updateAirplaneModeTracker() {
292         // Is Wi-Fi sensitive to airplane mode changes ?
293         if (!isAirplaneSensitive()) {
294             return false;
295         }
296 
297         mAirplaneModeOn = getPersistedAirplaneModeOn();
298         return true;
299     }
300 
301     synchronized void handleAirplaneModeToggled() {
302         if (mAirplaneModeOn) {
303             mApmEnabledTimeSinceBootMillis = mClock.getElapsedSinceBootMillis();
304             mIsWifiOnBeforeEnteringApm = mPersistWifiState == WIFI_ENABLED;
305             if (mPersistWifiState == WIFI_ENABLED) {
306                 if (mDeviceConfigFacade.isApmEnhancementEnabled()
307                         && getUserSecureIntegerSetting(WIFI_APM_STATE, WIFI_TURNS_OFF_IN_APM)
308                         == WIFI_REMAINS_ON_IN_APM) {
309                     persistWifiState(WIFI_ENABLED_APM_OVERRIDE);
310                     if (getUserSecureIntegerSetting(APM_WIFI_NOTIFICATION, NOTIFICATION_NOT_SHOWN)
311                             == NOTIFICATION_NOT_SHOWN
312                             && !isBluetoothEnabledOnApm()) {
313                         mWifiThreadRunner.post(
314                                 () -> showNotification(R.string.apm_enabled_first_time_title,
315                                         R.string.apm_enabled_first_time_message));
316                         setUserSecureIntegerSetting(APM_WIFI_NOTIFICATION, NOTIFICATION_SHOWN);
317                     }
318                 } else {
319                     // Wifi disabled due to airplane on
320                     persistWifiState(WIFI_DISABLED_APM_ON);
321                 }
322             }
323             mIsWifiOnAfterEnteringApm = mPersistWifiState == WIFI_ENABLED_APM_OVERRIDE;
324         } else {
mWifiMetrics.reportAirplaneModeSession(mIsWifiOnBeforeEnteringApm, mIsWifiOnAfterEnteringApm, mPersistWifiState == WIFI_ENABLED_APM_OVERRIDE, getUserSecureIntegerSetting(APM_WIFI_ENABLED_NOTIFICATION, NOTIFICATION_NOT_SHOWN) == NOTIFICATION_SHOWN, mUserToggledWifiDuringApm, mUserToggledWifiAfterEnteringApmWithinMinute)325             mWifiMetrics.reportAirplaneModeSession(mIsWifiOnBeforeEnteringApm,
326                     mIsWifiOnAfterEnteringApm,
327                     mPersistWifiState == WIFI_ENABLED_APM_OVERRIDE,
328                     getUserSecureIntegerSetting(APM_WIFI_ENABLED_NOTIFICATION,
329                             NOTIFICATION_NOT_SHOWN) == NOTIFICATION_SHOWN,
330                     mUserToggledWifiDuringApm, mUserToggledWifiAfterEnteringApmWithinMinute);
331             mUserToggledWifiDuringApm = false;
332             mUserToggledWifiAfterEnteringApmWithinMinute = false;
333 
334             /* On airplane mode disable, restore wifi state if necessary */
335             if (mPersistWifiState == WIFI_ENABLED_APM_OVERRIDE
336                     || mPersistWifiState == WIFI_DISABLED_APM_ON) {
337                 persistWifiState(WIFI_ENABLED);
338             }
339         }
340     }
341 
342     synchronized void handleWifiScanAlwaysAvailableToggled(boolean isAvailable) {
343         persistScanAlwaysAvailableState(isAvailable);
344     }
345 
346     synchronized boolean handleWifiScoringEnabled(boolean enabled) {
347         persistWifiScoringEnabledState(enabled);
348         return true;
349     }
350 
351     /**
352      * Handle the Wifi Passpoint enable/disable status change.
353      */
354     public synchronized void handleWifiPasspointEnabled(boolean enabled) {
355         persistWifiPasspointEnabledState(enabled);
356     }
357 
358     /**
359      * Handle the Wifi Multi Internet state change.
360      */
361     public synchronized void handleWifiMultiInternetMode(int mode) {
362         persistWifiMultiInternetMode(mode);
363     }
364 
365     /**
366      * Indicate whether Wi-Fi should remain on when airplane mode is enabled
367      */
368     public boolean shouldWifiRemainEnabledWhenApmEnabled() {
369         return mDeviceConfigFacade.isApmEnhancementEnabled()
370                 && isWifiToggleEnabled()
371                 && (getUserSecureIntegerSetting(WIFI_APM_STATE,
372                 WIFI_TURNS_OFF_IN_APM) == WIFI_REMAINS_ON_IN_APM);
373     }
374 
375     private boolean isBluetoothEnabledOnApm() {
376         return mFrameworkFacade.getIntegerSetting(mContext.getContentResolver(),
377                 Settings.Global.BLUETOOTH_ON, 0) != 0
378                 && getUserSecureIntegerSetting(BLUETOOTH_APM_STATE, BT_TURNS_OFF_IN_APM)
379                 == BT_REMAINS_ON_IN_APM;
380     }
381 
382     synchronized void updateSatelliteModeTracker() {
383         mSatelliteModeOn = getPersistedSatelliteModeOn();
384     }
385 
386     public boolean isSatelliteModeOn() {
387         return mSatelliteModeOn;
388     }
389 
390     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
391         pw.println("WifiState " + getPersistedWifiState());
392         pw.println("AirplaneModeOn " + getPersistedAirplaneModeOn());
393         pw.println("ScanAlwaysAvailable " + getPersistedScanAlwaysAvailable());
394         pw.println("WifiScoringState " + getPersistedWifiScoringEnabled());
395         pw.println("WifiPasspointState " + getPersistedWifiPasspointEnabled());
396         pw.println("WifiMultiInternetMode " + getPersistedWifiMultiInternetMode());
397         pw.println("WifiStateApm " + (getUserSecureIntegerSetting(WIFI_APM_STATE,
398                 WIFI_TURNS_OFF_IN_APM) == WIFI_REMAINS_ON_IN_APM));
399         pw.println("WifiStateBt " + isBluetoothEnabledOnApm());
400         pw.println("WifiStateUser " + ActivityManager.getCurrentUser());
401         pw.println("AirplaneModeEnhancementEnabled "
402                 + mDeviceConfigFacade.isApmEnhancementEnabled());
403         if (mAirplaneModeOn) {
404             pw.println("WifiOnBeforeEnteringApm " + mIsWifiOnBeforeEnteringApm);
405             pw.println("WifiOnAfterEnteringApm " + mIsWifiOnAfterEnteringApm);
406             pw.println("UserToggledWifiDuringApm " + mUserToggledWifiDuringApm);
407             pw.println("UserToggledWifiAfterEnteringApmWithinMinute "
408                     + mUserToggledWifiAfterEnteringApmWithinMinute);
409         }
410         pw.println("SatelliteModeOn " + mSatelliteModeOn);
411     }
412 
413     private void persistWifiState(int state) {
414         final ContentResolver cr = mContext.getContentResolver();
415         mPersistWifiState = state;
416         mFrameworkFacade.setIntegerSetting(cr, Settings.Global.WIFI_ON, state);
417     }
418 
419     private void persistScanAlwaysAvailableState(boolean isAvailable) {
420         mSettingsConfigStore.put(
421                 WifiSettingsConfigStore.WIFI_SCAN_ALWAYS_AVAILABLE, isAvailable);
422     }
423 
424     private void persistWifiScoringEnabledState(boolean enabled) {
425         mSettingsConfigStore.put(
426                 WifiSettingsConfigStore.WIFI_SCORING_ENABLED, enabled);
427     }
428 
429     private void persistWifiPasspointEnabledState(boolean enabled) {
430         mSettingsConfigStore.put(
431                 WifiSettingsConfigStore.WIFI_PASSPOINT_ENABLED, enabled);
432     }
433 
434     private void persistWifiMultiInternetMode(int mode) {
435         mSettingsConfigStore.put(
436                 WifiSettingsConfigStore.WIFI_MULTI_INTERNET_MODE, mode);
437     }
438 
439     /* Does Wi-Fi need to be disabled when airplane mode is on ? */
440     private boolean isAirplaneSensitive() {
441         String airplaneModeRadios = mFrameworkFacade.getStringSetting(mContext,
442                 Settings.Global.AIRPLANE_MODE_RADIOS);
443         return airplaneModeRadios == null
444                 || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI);
445     }
446 
447     /* Is Wi-Fi allowed to be re-enabled while airplane mode is on ? */
448     private boolean isAirplaneToggleable() {
449         String toggleableRadios = mFrameworkFacade.getStringSetting(mContext,
450                 Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
451         return toggleableRadios != null
452                 && toggleableRadios.contains(Settings.Global.RADIO_WIFI);
453     }
454 
455     private int getPersistedWifiState() {
456         final ContentResolver cr = mContext.getContentResolver();
457         try {
458             return mFrameworkFacade.getIntegerSetting(cr, Settings.Global.WIFI_ON);
459         } catch (Settings.SettingNotFoundException e) {
460             mFrameworkFacade.setIntegerSetting(cr, Settings.Global.WIFI_ON, WIFI_DISABLED);
461             return WIFI_DISABLED;
462         }
463     }
464 
465     private boolean getPersistedAirplaneModeOn() {
466         return mFrameworkFacade.getIntegerSetting(mContext.getContentResolver(),
467                 Settings.Global.AIRPLANE_MODE_ON, APM_DISABLED) == APM_ENABLED;
468     }
469 
470     private boolean getPersistedScanAlwaysAvailable() {
471         return mSettingsConfigStore.get(
472                 WifiSettingsConfigStore.WIFI_SCAN_ALWAYS_AVAILABLE);
473     }
474 
475     private boolean getPersistedWifiScoringEnabled() {
476         return mSettingsConfigStore.get(
477                 WifiSettingsConfigStore.WIFI_SCORING_ENABLED);
478     }
479 
480     private boolean getPersistedWifiPasspointEnabled() {
481         return mSettingsConfigStore.get(
482                 WifiSettingsConfigStore.WIFI_PASSPOINT_ENABLED);
483     }
484 
485     private int getPersistedWifiMultiInternetMode() {
486         return mSettingsConfigStore.get(
487                 WifiSettingsConfigStore.WIFI_MULTI_INTERNET_MODE);
488     }
489 
490     private boolean getPersistedIsSatelliteModeSensitive() {
491         String satelliteRadios = mFrameworkFacade.getStringSetting(mContext,
492                 SETTINGS_SATELLITE_MODE_RADIOS);
493         return satelliteRadios != null
494                 && satelliteRadios.contains(Settings.Global.RADIO_WIFI);
495     }
496 
497     /** Returns true if satellite mode is turned on. */
498     private boolean getPersistedSatelliteModeOn() {
499         if (!getPersistedIsSatelliteModeSensitive()) return false;
500         return  mFrameworkFacade.getIntegerSetting(
501                 mContext, SETTINGS_SATELLITE_MODE_ENABLED, 0) == 1;
502     }
503 }
504