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 
17 package com.android.server.wifi;
18 
19 import static android.app.Notification.VISIBILITY_SECRET;
20 
21 import android.app.Notification;
22 import android.app.PendingIntent;
23 import android.content.Intent;
24 import android.graphics.drawable.Icon;
25 import android.net.wifi.ScanResult;
26 import android.net.wifi.WifiContext;
27 import android.util.Log;
28 
29 import com.android.modules.utils.build.SdkLevel;
30 import com.android.wifi.resources.R;
31 
32 /**
33  * Helper to create notifications for {@link OpenNetworkNotifier}.
34  */
35 public class ConnectToNetworkNotificationBuilder {
36 
37     /** Intent when user dismissed the "Connect to Network" notification. */
38     public static final String ACTION_USER_DISMISSED_NOTIFICATION =
39             "com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION";
40 
41     /** Intent when user tapped action button to connect to recommended network. */
42     public static final String ACTION_CONNECT_TO_NETWORK =
43             "com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK";
44 
45     /** Intent when user tapped action button to open Wi-Fi Settings. */
46     public static final String ACTION_PICK_WIFI_NETWORK =
47             "com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK";
48 
49     /** Intent when user tapped "Failed to connect" notification to open Wi-Fi Settings. */
50     public static final String ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE =
51             "com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE";
52 
53     /** Extra data added to the Intent to specify the registering network notifier. */
54     public static final String AVAILABLE_NETWORK_NOTIFIER_TAG =
55             "com.android.server.wifi.ConnectToNetworkNotification.AVAILABLE_NETWORK_NOTIFIER_TAG";
56 
57     private final WifiContext mContext;
58     private final FrameworkFacade mFrameworkFacade;
59 
ConnectToNetworkNotificationBuilder( WifiContext context, FrameworkFacade framework)60     public ConnectToNetworkNotificationBuilder(
61             WifiContext context,
62             FrameworkFacade framework) {
63         mContext = context;
64         mFrameworkFacade = framework;
65     }
66 
67     /**
68      * Creates the connect to network notification that alerts users of a recommended connectable
69      * network.
70      *
71      * There are two actions - "Options" link to the Wi-Fi picker activity, and "Connect" prompts
72      * the connection to the recommended network.
73      *
74      * @param notifierTag Unique tag of calling network notifier
75      * @param network The network to be recommended
76      */
createConnectToAvailableNetworkNotification(String notifierTag, ScanResult network)77     public Notification createConnectToAvailableNetworkNotification(String notifierTag,
78             ScanResult network) {
79         CharSequence title;
80         switch (notifierTag) {
81             case OpenNetworkNotifier.TAG:
82                 title = mContext.getText(R.string.wifi_available_title);
83                 break;
84             default:
85                 Log.wtf("ConnectToNetworkNotificationBuilder", "Unknown network notifier."
86                         + notifierTag);
87                 return null;
88         }
89         Notification.Action.Builder connectActionBuilder =
90                 new Notification.Action.Builder(null /* icon */,
91                 mContext.getResources().getText(R.string.wifi_available_action_connect),
92                 getPrivateBroadcast(ACTION_CONNECT_TO_NETWORK, notifierTag));
93         // >= Android 12: Want the user to unlock before triggering connection.
94         if (SdkLevel.isAtLeastS()) {
95             connectActionBuilder.setAuthenticationRequired(true);
96         }
97         Notification.Action connectAction = connectActionBuilder.build();
98         Notification.Action allNetworksAction = new Notification.Action.Builder(null /* icon */,
99                 mContext.getResources().getText(R.string.wifi_available_action_all_networks),
100                 getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK, notifierTag)).build();
101         Notification.Builder notificationBuilder =
102                 createNotificationBuilder(title, network.SSID, notifierTag)
103                 .setContentIntent(getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK, notifierTag))
104                 .addAction(connectAction)
105                 .addAction(allNetworksAction);
106         // < Android 12: Hide the notification in lock screen since (setAuthenticationRequired) is
107         // not available.
108         if (!SdkLevel.isAtLeastS()) {
109             notificationBuilder.setVisibility(VISIBILITY_SECRET);
110         }
111         return notificationBuilder.build();
112     }
113 
114     /**
115      * Creates the notification that indicates the controller is attempting to connect to the
116      * recommended network.
117      *
118      * @param notifierTag Unique tag of the calling network notifier
119      * @param network The network to be recommended
120      */
createNetworkConnectingNotification(String notifierTag, ScanResult network)121     public Notification createNetworkConnectingNotification(String notifierTag,
122             ScanResult network) {
123         return createNotificationBuilder(
124                 mContext.getText(R.string.wifi_available_title_connecting), network.SSID,
125                         notifierTag)
126                 .setProgress(0 /* max */, 0 /* progress */, true /* indeterminate */)
127                 .build();
128     }
129 
130     /**
131      * Creates the notification that indicates the controller successfully connected to the
132      * recommended network.
133      *
134      * @param notifierTag Unique tag of the calling network notifier
135      * @param network The network to be recommended
136      */
createNetworkConnectedNotification(String notifierTag, ScanResult network)137     public Notification createNetworkConnectedNotification(String notifierTag, ScanResult network) {
138         return createNotificationBuilder(
139                 mContext.getText(R.string.wifi_available_title_connected), network.SSID,
140                         notifierTag)
141                 .build();
142     }
143 
144     /**
145      * Creates the notification that indicates the controller failed to connect to the recommended
146      * network. Tapping this notification opens the wifi picker.
147      *
148      * @param notifierTag Unique tag of the calling network notifier
149      */
createNetworkFailedNotification(String notifierTag)150     public Notification createNetworkFailedNotification(String notifierTag) {
151         return createNotificationBuilder(
152                 mContext.getText(R.string.wifi_available_title_failed_to_connect),
153                 mContext.getText(R.string.wifi_available_content_failed_to_connect), notifierTag)
154                 .setContentIntent(
155                         getPrivateBroadcast(ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE,
156                                 notifierTag))
157                 .setAutoCancel(true)
158                 .build();
159     }
160 
getNotifierRequestCode(String notifierTag)161     private int getNotifierRequestCode(String notifierTag) {
162         switch (notifierTag) {
163             case OpenNetworkNotifier.TAG:
164                 return 1;
165         }
166         return 0;
167     }
168 
createNotificationBuilder( CharSequence title, CharSequence content, String extraData)169     private Notification.Builder createNotificationBuilder(
170             CharSequence title, CharSequence content, String extraData) {
171         return mFrameworkFacade.makeNotificationBuilder(mContext,
172                 WifiService.NOTIFICATION_NETWORK_AVAILABLE)
173                 .setSmallIcon(Icon.createWithResource(mContext.getWifiOverlayApkPkgName(),
174                         com.android.wifi.resources.R.drawable.stat_notify_wifi_in_range))
175                 .setTicker(title)
176                 .setContentTitle(title)
177                 .setContentText(content)
178                 .setDeleteIntent(getPrivateBroadcast(ACTION_USER_DISMISSED_NOTIFICATION, extraData))
179                 .setShowWhen(false)
180                 .setLocalOnly(true)
181                 .setColor(mContext.getResources().getColor(
182                         android.R.color.system_notification_accent_color,
183                         mContext.getTheme()));
184     }
185 
getPrivateBroadcast(String action, String extraData)186     private PendingIntent getPrivateBroadcast(String action, String extraData) {
187         Intent intent = new Intent(action).setPackage(mContext.getServiceWifiPackageName());
188         int requestCode = 0;  // Makes the different kinds of notifications distinguishable
189         if (extraData != null) {
190             intent.putExtra(AVAILABLE_NETWORK_NOTIFIER_TAG, extraData);
191             requestCode = getNotifierRequestCode(extraData);
192         }
193         return mFrameworkFacade.getBroadcast(mContext, requestCode, intent,
194                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
195     }
196 }
197