1 /*
2  * Copyright (C) 2023 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.google.snippet.wifi.direct;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.net.NetworkInfo;
24 import android.net.wifi.p2p.WifiP2pManager;
25 
26 import androidx.test.core.app.ApplicationProvider;
27 
28 import com.google.android.mobly.snippet.Snippet;
29 import com.google.android.mobly.snippet.event.EventCache;
30 import com.google.android.mobly.snippet.event.SnippetEvent;
31 import com.google.android.mobly.snippet.rpc.AsyncRpc;
32 import com.google.android.mobly.snippet.rpc.Rpc;
33 import com.google.android.mobly.snippet.rpc.RpcOptional;
34 import com.google.android.mobly.snippet.util.Log;
35 
36 import org.json.JSONException;
37 import org.json.JSONObject;
38 
39 /** Snippet class for WifiP2pManager. */
40 public class WifiP2pManagerSnippet implements Snippet {
41     private static final String WIFI_P2P_CREATING_GROUP = "CREATING_GROUP";
42 
43     private final Context mContext;
44     private final IntentFilter mIntentFilter;
45     private final WifiP2pManager mWifiP2pManager;
46 
47     private WifiP2pManager.Channel mChannel = null;
48     private WifiP2pStateChangedReceiver mStateChangedReceiver = null;
49 
50     private static class WifiP2pManagerException extends Exception {
WifiP2pManagerException(String message)51         WifiP2pManagerException(String message) {
52             super(message);
53         }
54     }
55 
WifiP2pManagerSnippet()56     public WifiP2pManagerSnippet() {
57         mContext = ApplicationProvider.getApplicationContext();
58         mWifiP2pManager = mContext.getSystemService(WifiP2pManager.class);
59 
60         mIntentFilter = new IntentFilter();
61         mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
62         mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
63         mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
64         mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
65     }
66 
67     @AsyncRpc(description = "Registers the application with the Wi-Fi framework.")
p2pInitialize(String callbackId)68     public void p2pInitialize(String callbackId) {
69         if (mChannel != null) {
70             Log.d("Channel has already created, skip WifiP2pManager.initialize()");
71             return;
72         }
73         mChannel = mWifiP2pManager.initialize(mContext, mContext.getMainLooper(), null);
74         mStateChangedReceiver = new WifiP2pStateChangedReceiver(callbackId, mIntentFilter);
75     }
76 
77     @Rpc(
78             description =
79                     "Close the current P2P connection and indicate to the P2P service that"
80                             + " connections created by the app can be removed.")
p2pClose()81     public void p2pClose() {
82         if (mChannel == null) {
83             Log.d("Channel has already closed, skip" + " WifiP2pManager.Channel.close()");
84             return;
85         }
86         mChannel.close();
87         mChannel = null;
88         mStateChangedReceiver.close();
89         mStateChangedReceiver = null;
90     }
91 
92     @AsyncRpc(description = "Create a p2p group with the current device as the group owner.")
p2pCreateGroup(String callbackId, @RpcOptional JSONObject wifiP2pConfig)93     public void p2pCreateGroup(String callbackId, @RpcOptional JSONObject wifiP2pConfig)
94             throws JSONException, WifiP2pManagerException {
95         if (mChannel == null) {
96             throw new WifiP2pManagerException(
97                     "Channel has not created, call p2pInitialize() first");
98         }
99         WifiP2pActionListener actionListener = new WifiP2pActionListener(callbackId);
100         if (wifiP2pConfig == null) {
101             mWifiP2pManager.createGroup(mChannel, actionListener);
102         } else {
103             mWifiP2pManager.createGroup(
104                     mChannel, JsonDeserializer.jsonToWifiP2pConfig(wifiP2pConfig), actionListener);
105         }
106     }
107 
108     @AsyncRpc(description = "Start a p2p connection to a device with the specified configuration.")
p2pConnect(String callbackId, JSONObject wifiP2pConfig)109     public void p2pConnect(String callbackId, JSONObject wifiP2pConfig)
110             throws JSONException, WifiP2pManagerException {
111         if (mChannel == null) {
112             throw new WifiP2pManagerException(
113                     "Channel has not created, call p2pInitialize() first");
114         }
115         WifiP2pActionListener actionListener = new WifiP2pActionListener(callbackId);
116         mWifiP2pManager.connect(
117                 mChannel, JsonDeserializer.jsonToWifiP2pConfig(wifiP2pConfig), actionListener);
118     }
119 
120     private class WifiP2pStateChangedReceiver extends BroadcastReceiver {
121         public final String callbackId;
122 
WifiP2pStateChangedReceiver(String callbackId, IntentFilter mIntentFilter)123         private WifiP2pStateChangedReceiver(String callbackId, IntentFilter mIntentFilter) {
124             this.callbackId = callbackId;
125             mContext.registerReceiver(this, mIntentFilter, Context.RECEIVER_NOT_EXPORTED);
126         }
127 
close()128         private void close() {
129             mContext.unregisterReceiver(this);
130         }
131 
132         @Override
onReceive(Context mContext, Intent intent)133         public void onReceive(Context mContext, Intent intent) {
134             String action = intent.getAction();
135             SnippetEvent event = new SnippetEvent(callbackId, action);
136             switch (action) {
137                 case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
138                     event
139                             .getData()
140                             .putInt(
141                                     WifiP2pManager.EXTRA_WIFI_STATE,
142                                     intent.getIntExtra(
143                                             WifiP2pManager.EXTRA_WIFI_STATE, 0));
144                     break;
145                 case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
146                     event
147                             .getData()
148                             .putParcelable(
149                                     action,
150                                     intent.getParcelableExtra(
151                                             WifiP2pManager.EXTRA_P2P_DEVICE_LIST));
152                     break;
153                 case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
154                     NetworkInfo networkInfo = intent.getParcelableExtra(
155                             WifiP2pManager.EXTRA_NETWORK_INFO);
156                     if (networkInfo.isConnected()) {
157                         SnippetEvent connectedEvent = new SnippetEvent(
158                                 callbackId, WIFI_P2P_CREATING_GROUP);
159                         connectedEvent
160                                 .getData()
161                                 .putParcelable(
162                                         WifiP2pManager.EXTRA_WIFI_P2P_INFO,
163                                         intent.getParcelableExtra(
164                                                 WifiP2pManager.EXTRA_WIFI_P2P_INFO));
165                         connectedEvent
166                                 .getData()
167                                 .putParcelable(
168                                         WifiP2pManager.EXTRA_WIFI_P2P_GROUP,
169                                         intent.getParcelableExtra(
170                                                 WifiP2pManager.EXTRA_WIFI_P2P_GROUP));
171                         EventCache.getInstance().postEvent(connectedEvent);
172                     }
173                     break;
174                 case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
175                     event
176                             .getData()
177                             .putParcelable(
178                                     action,
179                                     intent.getParcelableExtra(
180                                             WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
181                     break;
182                 case WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:
183                     event
184                             .getData()
185                             .putInt(action, intent.getIntExtra(
186                                     WifiP2pManager.EXTRA_DISCOVERY_STATE, 0));
187                     break;
188                 default:
189                     break;
190             }
191             EventCache.getInstance().postEvent(event);
192         }
193     }
194 
195     private static class WifiP2pActionListener implements WifiP2pManager.ActionListener {
196         private final String mCallbackId;
197 
WifiP2pActionListener(String callbackId)198         WifiP2pActionListener(String callbackId) {
199             this.mCallbackId = callbackId;
200         }
201 
202         @Override
onSuccess()203         public void onSuccess() {
204             SnippetEvent event = new SnippetEvent(mCallbackId, "onSuccess");
205             EventCache.getInstance().postEvent(event);
206         }
207 
208         @Override
onFailure(int reason)209         public void onFailure(int reason) {
210             SnippetEvent event = new SnippetEvent(mCallbackId, "onFailure");
211             event.getData().putInt("reason", reason);
212             EventCache.getInstance().postEvent(event);
213         }
214     }
215 
216     @Override
shutdown()217     public void shutdown() {
218         p2pClose();
219     }
220 }
221 
222