• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 static com.android.server.wifi.WakeupNotificationFactory.ACTION_DISMISS_NOTIFICATION;
20 import static com.android.server.wifi.WakeupNotificationFactory.ACTION_OPEN_WIFI_PREFERENCES;
21 import static com.android.server.wifi.WakeupNotificationFactory.ACTION_TURN_OFF_WIFI_WAKE;
22 
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.net.wifi.WifiContext;
28 import android.os.Handler;
29 import android.os.SystemClock;
30 import android.provider.Settings;
31 import android.text.format.DateUtils;
32 import android.util.Log;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 /**
37  * Manages the WiFi Wake onboarding notification.
38  *
39  * <p>If a user disables wifi with Wifi Wake enabled, this notification is shown to explain that
40  * wifi may turn back on automatically. It will be displayed up to 3 times, or until the
41  * user either interacts with the onboarding notification in some way (e.g. dismiss, tap) or
42  * manually enables/disables the feature in WifiSettings.
43  */
44 public class WakeupOnboarding {
45 
46     private static final String TAG = "WakeupOnboarding";
47 
48     @VisibleForTesting
49     static final int NOTIFICATIONS_UNTIL_ONBOARDED = 3;
50     @VisibleForTesting
51     static final long REQUIRED_NOTIFICATION_DELAY = DateUtils.DAY_IN_MILLIS;
52     private static final long NOT_SHOWN_TIMESTAMP = -1;
53 
54     private final WifiContext mContext;
55     private final WakeupNotificationFactory mWakeupNotificationFactory;
56     private final WifiNotificationManager mNotificationManager;
57     private final Handler mHandler;
58     private final WifiConfigManager mWifiConfigManager;
59     private final IntentFilter mIntentFilter;
60     private final FrameworkFacade mFrameworkFacade;
61 
62     private boolean mIsOnboarded;
63     private int mTotalNotificationsShown;
64     private long mLastShownTimestamp = NOT_SHOWN_TIMESTAMP;
65     private boolean mIsNotificationShowing;
66 
67     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
68         @Override
69         public void onReceive(Context context, Intent intent) {
70             switch (intent.getAction()) {
71                 case ACTION_TURN_OFF_WIFI_WAKE:
72                     mFrameworkFacade.setIntegerSetting(mContext,
73                             Settings.Global.WIFI_WAKEUP_ENABLED, 0);
74                     dismissNotification(true /* shouldOnboard */);
75                     break;
76                 case ACTION_OPEN_WIFI_PREFERENCES:
77                     // Close notification drawer before opening preferences.
78                     mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
79                     mContext.startActivity(new Intent(Settings.ACTION_WIFI_IP_SETTINGS)
80                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
81                     dismissNotification(true /* shouldOnboard */);
82                     break;
83                 case ACTION_DISMISS_NOTIFICATION:
84                     dismissNotification(true /* shouldOnboard */);
85                     break;
86                 default:
87                     Log.e(TAG, "Unknown action " + intent.getAction());
88             }
89         }
90     };
91 
WakeupOnboarding( WifiContext context, WifiConfigManager wifiConfigManager, Handler handler, FrameworkFacade frameworkFacade, WakeupNotificationFactory wakeupNotificationFactory, WifiNotificationManager wifiNotificationManager)92     public WakeupOnboarding(
93             WifiContext context,
94             WifiConfigManager wifiConfigManager,
95             Handler handler,
96             FrameworkFacade frameworkFacade,
97             WakeupNotificationFactory wakeupNotificationFactory,
98             WifiNotificationManager wifiNotificationManager) {
99         mContext = context;
100         mWifiConfigManager = wifiConfigManager;
101         mHandler = handler;
102         mFrameworkFacade = frameworkFacade;
103         mWakeupNotificationFactory = wakeupNotificationFactory;
104         mNotificationManager = wifiNotificationManager;
105 
106         mIntentFilter = new IntentFilter();
107         mIntentFilter.addAction(ACTION_TURN_OFF_WIFI_WAKE);
108         mIntentFilter.addAction(ACTION_DISMISS_NOTIFICATION);
109         mIntentFilter.addAction(ACTION_OPEN_WIFI_PREFERENCES);
110     }
111 
112     /** Returns whether the user is onboarded. */
isOnboarded()113     public boolean isOnboarded() {
114         return mIsOnboarded;
115     }
116 
117     /** Shows the onboarding notification if applicable. */
maybeShowNotification()118     public void maybeShowNotification() {
119         maybeShowNotification(SystemClock.elapsedRealtime());
120     }
121 
122     @VisibleForTesting
maybeShowNotification(long timestamp)123     void maybeShowNotification(long timestamp) {
124         if (!shouldShowNotification(timestamp)) {
125             return;
126         }
127         Log.d(TAG, "Showing onboarding notification.");
128 
129         incrementTotalNotificationsShown();
130         mIsNotificationShowing = true;
131         mLastShownTimestamp = timestamp;
132 
133         mContext.registerReceiver(mBroadcastReceiver, mIntentFilter,
134                 null /* broadcastPermission */, mHandler);
135         mNotificationManager.notify(WakeupNotificationFactory.ONBOARD_ID,
136                 mWakeupNotificationFactory.createOnboardingNotification());
137     }
138 
139     /**
140      * Increment the total number of shown notifications and onboard the user if reached the
141      * required amount.
142      */
incrementTotalNotificationsShown()143     private void incrementTotalNotificationsShown() {
144         mTotalNotificationsShown++;
145         if (mTotalNotificationsShown >= NOTIFICATIONS_UNTIL_ONBOARDED) {
146             setOnboarded();
147         } else {
148             mWifiConfigManager.saveToStore(false /* forceWrite */);
149         }
150     }
151 
shouldShowNotification(long timestamp)152     private boolean shouldShowNotification(long timestamp) {
153         if (isOnboarded() || mIsNotificationShowing) {
154             return false;
155         }
156 
157         return mLastShownTimestamp == NOT_SHOWN_TIMESTAMP
158                 || (timestamp - mLastShownTimestamp) > REQUIRED_NOTIFICATION_DELAY;
159     }
160 
161     /** Handles onboarding cleanup on stop. */
onStop()162     public void onStop() {
163         dismissNotification(false /* shouldOnboard */);
164     }
165 
dismissNotification(boolean shouldOnboard)166     private void dismissNotification(boolean shouldOnboard) {
167         if (!mIsNotificationShowing) {
168             return;
169         }
170 
171         if (shouldOnboard) {
172             setOnboarded();
173         }
174 
175         mContext.unregisterReceiver(mBroadcastReceiver);
176         mNotificationManager.cancel(WakeupNotificationFactory.ONBOARD_ID);
177         mIsNotificationShowing = false;
178     }
179 
180     /** Sets the user as onboarded and persists to store. */
setOnboarded()181     public void setOnboarded() {
182         if (mIsOnboarded) {
183             return;
184         }
185         Log.d(TAG, "Setting user as onboarded.");
186         mIsOnboarded = true;
187         mWifiConfigManager.saveToStore(false /* forceWrite */);
188     }
189 
190     /** Returns the {@link WakeupConfigStoreData.DataSource} for the onboarded status. */
getIsOnboadedDataSource()191     public WakeupConfigStoreData.DataSource<Boolean> getIsOnboadedDataSource() {
192         return new IsOnboardedDataSource();
193     }
194 
195     /** Returns the {@link WakeupConfigStoreData.DataSource} for the notification status. */
getNotificationsDataSource()196     public WakeupConfigStoreData.DataSource<Integer> getNotificationsDataSource() {
197         return new NotificationsDataSource();
198     }
199 
200     private class IsOnboardedDataSource implements WakeupConfigStoreData.DataSource<Boolean> {
201 
202         @Override
getData()203         public Boolean getData() {
204             return mIsOnboarded;
205         }
206 
207         @Override
setData(Boolean data)208         public void setData(Boolean data) {
209             mIsOnboarded = data;
210         }
211     }
212 
213     private class NotificationsDataSource implements WakeupConfigStoreData.DataSource<Integer> {
214 
215         @Override
getData()216         public Integer getData() {
217             return mTotalNotificationsShown;
218         }
219 
220         @Override
setData(Integer data)221         public void setData(Integer data) {
222             mTotalNotificationsShown = data;
223         }
224     }
225 }
226