Home | History | Annotate | Download | only in wakeup
      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 
    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      */
    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 
    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 
    216     private void endWifiSession() {
    217         if (mWifiSessionStarted) {
    218             mWifiSessionStarted = false;
    219             cancelNotificationIfNeeded();
    220             mConnectedSsid = null;
    221             mContext.unregisterReceiver(mBroadcastReceiver);
    222         }
    223     }
    224 
    225     private void cancelNotificationIfNeeded() {
    226         if (mNotificationShown) {
    227             mNotificationShown = false;
    228             mNotificationManager.cancel(TAG, NOTIFICATION_ID);
    229         }
    230     }
    231 }
    232