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