• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.networkrecommendation.wakeup;
17 
18 import static com.android.networkrecommendation.Constants.TAG;
19 import static com.android.networkrecommendation.util.NotificationChannelUtil.CHANNEL_ID_WAKEUP;
20 
21 import android.app.Notification;
22 import android.app.NotificationManager;
23 import android.app.PendingIntent;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.res.Resources;
29 import android.net.wifi.WifiConfiguration;
30 import android.net.wifi.WifiInfo;
31 import android.net.wifi.WifiManager;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.provider.Settings;
35 import android.support.annotation.NonNull;
36 import android.support.annotation.VisibleForTesting;
37 import android.text.TextUtils;
38 import android.util.ArraySet;
39 import com.android.networkrecommendation.R;
40 import com.android.networkrecommendation.config.G;
41 import com.android.networkrecommendation.config.Preferences;
42 import com.android.networkrecommendation.scoring.util.HashUtil;
43 import com.android.networkrecommendation.util.Blog;
44 import com.android.networkrecommendation.util.NotificationChannelUtil;
45 import java.util.Set;
46 import java.util.concurrent.TimeUnit;
47 
48 /**
49  * Helper class for logging Wi-Fi Wakeup sessions and showing showing notifications for {@link
50  * WifiWakeupController}.
51  */
52 public class WifiWakeupHelper {
53     /** Unique ID used for the Wi-Fi Enabled notification. */
54     private static final int NOTIFICATION_ID = R.string.wifi_wakeup_enabled_notification_title;
55 
56     @VisibleForTesting
57     static final String ACTION_WIFI_SETTINGS =
58             "com.android.networkrecommendation.wakeup.ACTION_WIFI_SETTINGS";
59 
60     @VisibleForTesting
61     static final String ACTION_DISMISS_WIFI_ENABLED_NOTIFICATION =
62             "com.android.networkrecommendation.wakeup.ACTION_DISMISS_WIFI_ENABLED_NOTIFICATION";
63 
64     private static final long NETWORK_CONNECTED_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(30);
65     private static final IntentFilter INTENT_FILTER = new IntentFilter();
66 
67     static {
68         INTENT_FILTER.addAction(ACTION_DISMISS_WIFI_ENABLED_NOTIFICATION);
69         INTENT_FILTER.addAction(ACTION_WIFI_SETTINGS);
70         INTENT_FILTER.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
71     }
72 
73     private final Context mContext;
74     private final Resources mResources;
75     private final NotificationManager mNotificationManager;
76     private final Handler mHandler;
77     private final WifiManager mWifiManager;
78 
79     /** Whether the wakeup notification is currently displayed. */
80     private boolean mNotificationShown;
81     /** True when the device is still connected to the first connected ssid since wakeup. */
82     private boolean mWifiSessionStarted;
83     /** The first connected ssid after wakeup enabled wifi. */
84     private String mConnectedSsid;
85 
86     private final BroadcastReceiver mBroadcastReceiver =
87             new BroadcastReceiver() {
88                 @Override
89                 public void onReceive(Context context, Intent intent) {
90                     try {
91                         if (ACTION_WIFI_SETTINGS.equals(intent.getAction())) {
92                             mContext.startActivity(
93                                     new Intent(Settings.ACTION_WIFI_SETTINGS)
94                                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
95                         } else if (ACTION_DISMISS_WIFI_ENABLED_NOTIFICATION.equals(
96                                 intent.getAction())) {
97                             cancelNotificationIfNeeded();
98                         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(
99                                 intent.getAction())) {
100                             networkStateChanged();
101                         }
102                     } catch (RuntimeException re) {
103                         // TODO(b/35044022) Remove try/catch after a couple of releases when we are confident
104                         // this is not going to throw.
105                         Blog.e(TAG, re, "RuntimeException in broadcast receiver.");
106                     }
107                 }
108             };
109 
WifiWakeupHelper( Context context, Resources resources, Handler handler, NotificationManager notificationManager, WifiManager wifiManager)110     public WifiWakeupHelper(
111             Context context,
112             Resources resources,
113             Handler handler,
114             NotificationManager notificationManager,
115             WifiManager wifiManager) {
116         mContext = context;
117         mResources = resources;
118         mNotificationManager = notificationManager;
119         mHandler = handler;
120         mWifiManager = wifiManager;
121         mWifiSessionStarted = false;
122         mNotificationShown = false;
123         mConnectedSsid = null;
124     }
125 
126     /**
127      * Start tracking a wifi wakeup session. Optionally show a notification that Wi-Fi has been
128      * enabled by Wi-Fi Wakeup if one has not been displayed for this {@link WifiConfiguration}.
129      *
130      * @param wifiConfiguration the {@link WifiConfiguration} that triggered Wi-Fi to wakeup
131      */
startWifiSession(@onNull WifiConfiguration wifiConfiguration)132     public void startWifiSession(@NonNull WifiConfiguration wifiConfiguration) {
133         mContext.registerReceiver(
134                 mBroadcastReceiver, INTENT_FILTER, null /* broadcastPermission*/, mHandler);
135         mWifiSessionStarted = true;
136         mHandler.postDelayed(
137                 () -> {
138                     if (mWifiSessionStarted && mConnectedSsid == null) {
139                         endWifiSession();
140                     }
141                 },
142                 NETWORK_CONNECTED_TIMEOUT_MILLIS);
143 
144         Set<String> hashedSsidSet = Preferences.ssidsForWakeupShown.get();
145         String hashedSsid = HashUtil.getSsidHash(wifiConfiguration.SSID);
146         if (hashedSsidSet.isEmpty()) {
147             hashedSsidSet = new ArraySet<>();
148         } else if (hashedSsidSet.contains(hashedSsid)) {
149             Blog.i(
150                     TAG,
151                     "Already showed Wi-Fi Enabled notification for ssid: %s",
152                     Blog.pii(wifiConfiguration.SSID, G.Netrec.enableSensitiveLogging.get()));
153             return;
154         }
155         hashedSsidSet.add(hashedSsid);
156         Preferences.ssidsForWakeupShown.put(hashedSsidSet);
157 
158         String title = mResources.getString(R.string.wifi_wakeup_enabled_notification_title);
159         String summary =
160                 mResources.getString(
161                         R.string.wifi_wakeup_enabled_notification_context, wifiConfiguration.SSID);
162         PendingIntent savedNetworkSettingsPendingIntent =
163                 PendingIntent.getBroadcast(
164                         mContext,
165                         0,
166                         new Intent(ACTION_WIFI_SETTINGS),
167                         PendingIntent.FLAG_UPDATE_CURRENT);
168         PendingIntent deletePendingIntent =
169                 PendingIntent.getBroadcast(
170                         mContext,
171                         0,
172                         new Intent(ACTION_DISMISS_WIFI_ENABLED_NOTIFICATION),
173                         PendingIntent.FLAG_UPDATE_CURRENT);
174         Bundle extras = new Bundle();
175         extras.putString(
176                 Notification.EXTRA_SUBSTITUTE_APP_NAME,
177                 mResources.getString(R.string.notification_channel_group_name));
178         int smallIcon = R.drawable.ic_signal_wifi_statusbar_not_connected;
179         Notification.Builder notificationBuilder =
180                 new Notification.Builder(mContext)
181                         .setContentTitle(title)
182                         .setSmallIcon(smallIcon)
183                         .setColor(mContext.getColor(R.color.color_tint))
184                         .setStyle(new Notification.BigTextStyle().bigText(summary))
185                         .setAutoCancel(true)
186                         .setShowWhen(false)
187                         .setDeleteIntent(deletePendingIntent)
188                         .setPriority(Notification.PRIORITY_LOW)
189                         .setVisibility(Notification.VISIBILITY_PUBLIC)
190                         .setCategory(Notification.CATEGORY_STATUS)
191                         .setContentIntent(savedNetworkSettingsPendingIntent)
192                         .setLocalOnly(true)
193                         .addExtras(extras);
194         NotificationChannelUtil.setChannel(notificationBuilder, CHANNEL_ID_WAKEUP);
195         mNotificationManager.notify(TAG, NOTIFICATION_ID, notificationBuilder.build());
196         mNotificationShown = true;
197     }
198 
networkStateChanged()199     private void networkStateChanged() {
200         if (!mWifiManager.isWifiEnabled()) {
201             endWifiSession();
202             return;
203         }
204 
205         WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
206         String ssid = wifiInfo == null ? null : wifiInfo.getSSID();
207         if (mConnectedSsid == null) {
208             if (!TextUtils.isEmpty(ssid)) {
209                 mConnectedSsid = ssid;
210             }
211         } else if (!TextUtils.equals(ssid, mConnectedSsid)) {
212             endWifiSession();
213         }
214     }
215 
endWifiSession()216     private void endWifiSession() {
217         if (mWifiSessionStarted) {
218             mWifiSessionStarted = false;
219             cancelNotificationIfNeeded();
220             mConnectedSsid = null;
221             mContext.unregisterReceiver(mBroadcastReceiver);
222         }
223     }
224 
cancelNotificationIfNeeded()225     private void cancelNotificationIfNeeded() {
226         if (mNotificationShown) {
227             mNotificationShown = false;
228             mNotificationManager.cancel(TAG, NOTIFICATION_ID);
229         }
230     }
231 }
232