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