• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.WifiManager;
25 import android.net.wifi.p2p.WifiP2pConfig;
26 import android.net.wifi.p2p.WifiP2pDevice;
27 import android.net.wifi.p2p.WifiP2pGroup;
28 import android.net.wifi.p2p.WifiP2pInfo;
29 import android.net.wifi.p2p.WifiP2pManager;
30 import android.os.Build;
31 import android.util.Log;
32 
33 import androidx.test.platform.app.InstrumentationRegistry;
34 
35 import com.android.compatibility.common.util.ApiLevelUtil;
36 import com.android.compatibility.common.util.PollingCheck;
37 import com.android.compatibility.common.util.ShellIdentityUtils;
38 
39 import com.google.android.mobly.snippet.Snippet;
40 import com.google.android.mobly.snippet.rpc.Rpc;
41 
42 import org.json.JSONException;
43 
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.TimeUnit;
46 
47 public class WifiDirectSnippet implements Snippet {
48 
49     private static final String TAG = "WifiDirectSnippet";
50     private final Context mContext;
51 
52     private final WifiP2pManager mP2pManager;
53     private WifiP2pInfo mP2pInfo;
54     private WifiP2pGroup mP2pGroup;
55     private WifiP2pManager.Channel mChannel;
56     private WifiManager mWifiManager;
57 
58     private final Object mLock = new Object();
59 
60     private final WifiP2pManager.ChannelListener mChannelListener =
61             new WifiP2pManager.ChannelListener() {
62                 public void onChannelDisconnected() {
63                     Log.e(TAG, "P2P channel disconnected");
64                 }
65             };
66 
67     private BroadcastReceiver mP2pStateChangedReceiver =
68             new BroadcastReceiver() {
69         @Override
70         public void onReceive(Context c, Intent intent) {
71             String action = intent.getAction();
72             if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
73                 Log.d(TAG, "Wifi P2p State Changed.");
74                 int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, 0);
75                 if (state == WifiP2pManager.WIFI_P2P_STATE_DISABLED) {
76                     Log.d(TAG, "Disabled");
77                 } else if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
78                     Log.d(TAG, "Enabled");
79                 }
80             } else if (action.equals(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)) {
81                 Log.d(TAG, "Wifi P2p Peers Changed");
82             } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
83                 Log.d(TAG, "Wifi P2p Connection Changed.");
84                 synchronized (mLock) {
85                     NetworkInfo networkInfo = intent
86                             .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
87                     if (networkInfo.isConnected()) {
88                         Log.d(TAG, "Wifi P2p Connected.");
89                         mP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
90                         mP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
91                         mLock.notify();
92                     } else if (networkInfo.isConnectedOrConnecting()) {
93                         Log.d(TAG, "Wifi P2p is in connecting state");
94                     } else {
95                         Log.d(TAG, "Wifi P2p is in disconnected state");
96                     }
97                 }
98             } else if (action.equals(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)) {
99                 Log.d(TAG, "Wifi P2p This Device Changed.");
100                 WifiP2pDevice device = intent
101                         .getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
102             } else if (action.equals(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION)) {
103                 Log.d(TAG, "Wifi P2p Discovery Changed.");
104                 int state = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, 0);
105                 if (state == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
106                     Log.d(TAG, "discovery started.");
107                 } else if (state == WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED) {
108                     Log.d(TAG, "discovery stopped.");
109                 }
110             }
111         }
112     };
113 
114     class WifiP2pActionListener implements WifiP2pManager.ActionListener {
115         private final String mOperation;
116         private final CountDownLatch mCountDownLatch;
WifiP2pActionListener(String action)117         WifiP2pActionListener(String action) {
118             mCountDownLatch = new CountDownLatch(1);
119             mOperation = action;
120         }
121 
await()122         public void await() throws InterruptedException {
123             mCountDownLatch.await(1000, TimeUnit.MILLISECONDS);
124         }
125 
126         @Override
onSuccess()127         public void onSuccess() {
128             Log.d(TAG, mOperation + " OnSuccess");
129             mCountDownLatch.countDown();
130         }
131         @Override
onFailure(int reason)132         public void onFailure(int reason) {
133             Log.d(TAG, mOperation + " onFailure - reason: " + reason);
134             mCountDownLatch.countDown();
135         }
136     }
137 
buildWifiP2pConfig(String networkName, String passphrase, int frequency)138     private WifiP2pConfig buildWifiP2pConfig(String networkName, String passphrase,
139             int frequency) {
140         WifiP2pConfig.Builder builder = new WifiP2pConfig.Builder();
141         builder.setNetworkName(networkName);
142         builder.setPassphrase(passphrase);
143         if (frequency != 0) {
144             builder.setGroupOperatingFrequency(frequency);
145         }
146         return builder.build();
147     }
148 
WifiDirectSnippet()149     public WifiDirectSnippet() {
150         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
151         mP2pManager = mContext.getSystemService(WifiP2pManager.class);
152         mWifiManager = mContext.getSystemService(WifiManager.class);
153 
154         Log.d(TAG, "WifiDirectSnippet - init");
155     }
156 
157     @Rpc(description = "Check if Aware pairing supported")
isChannelConstrainedDiscoverySupported()158     public boolean isChannelConstrainedDiscoverySupported()
159             throws InterruptedException {
160         if (!ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
161             return false;
162         }
163         return mP2pManager.isChannelConstrainedDiscoverySupported();
164     }
165 
166     @Rpc(description = "Execute p2p initialize")
initializeP2p()167     public void initializeP2p() throws Exception {
168         ShellIdentityUtils.invokeWithShellPermissions(
169                 () -> mWifiManager.allowAutojoinGlobal(false));
170         ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.disconnect());
171         PollingCheck.check(
172                 "Wifi not disconnected",
173                 20000,
174                 () -> mWifiManager.getConnectionInfo().getNetworkId() == -1);
175         mChannel = mP2pManager.initialize(mContext,  mContext.getMainLooper(), mChannelListener);
176         IntentFilter intentFilter = new IntentFilter(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
177         intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
178         intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
179         intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
180         intentFilter.setPriority(999);
181         mContext.registerReceiver(mP2pStateChangedReceiver, intentFilter);
182         Log.d(TAG, "Wifi Direct initialization completed");
183     }
184 
185     @Rpc(description = "Close P2P channel")
closeP2p()186     public void closeP2p() throws JSONException {
187         if (mChannel != null) {
188             try {
189                 WifiP2pActionListener actionListener = new WifiP2pActionListener("RemoveGroup");
190                 mP2pManager.removeGroup(mChannel, actionListener);
191                 actionListener.await();
192             } catch (InterruptedException e) {
193                 Log.d(TAG, "RemoveGroup request failed");
194             }
195             mChannel.close();
196             mChannel = null;
197             Log.d(TAG, "Wifi Direct close called");
198         }
199         ShellIdentityUtils.invokeWithShellPermissions(() -> mWifiManager.allowAutojoinGlobal(true));
200         Log.d(TAG, "Wifi Direct close completed");
201     }
202 
203     @Rpc(description = "Create a P2P group owner with config")
p2pCreateGroup(String networkName, String passphrase, int frequency)204     public boolean p2pCreateGroup(String networkName, String passphrase,
205             int frequency) throws JSONException {
206         if (mChannel == null) {
207             Log.d(TAG, "p2pCreateGroup failed -should call initializeP2p first ");
208             return false;
209         }
210         WifiP2pActionListener actionListener = new WifiP2pActionListener("CreateGroup");
211         WifiP2pConfig wifiP2pConfig = buildWifiP2pConfig(networkName, passphrase, frequency);
212         try {
213             mP2pManager.createGroup(mChannel, wifiP2pConfig, actionListener);
214             actionListener.await();
215         } catch (InterruptedException e) {
216             Log.d(TAG, "p2pCreateGroup request failed");
217             return false;
218         }
219         Log.d(TAG, "p2pCreateGroup succeeded");
220         return true;
221     }
222 
223     @Rpc(description = "Connect to a P2P group owner with config")
p2pConnect(String networkName, String passphrase, int frequency)224     public long p2pConnect(String networkName, String passphrase,
225             int frequency) throws JSONException, InterruptedException {
226         if (mChannel == null) {
227             Log.d(TAG, "p2pConnect failed- should call initializeP2p first");
228             return -1;
229         }
230         WifiP2pActionListener actionListener = new WifiP2pActionListener("Connect");
231         WifiP2pConfig wifiP2pConfig = buildWifiP2pConfig(networkName, passphrase, frequency);
232         long startTime = System.currentTimeMillis();
233         try {
234             mP2pManager.connect(mChannel, wifiP2pConfig, actionListener);
235             actionListener.await();
236         } catch (InterruptedException e) {
237             Log.d(TAG, "p2pConnect request failed");
238             return -1;
239         }
240 
241         synchronized (mLock) {
242             mLock.wait(5000);
243         }
244 
245         if (mP2pInfo == null || !mP2pInfo.groupFormed || mP2pGroup == null) {
246             Log.d(TAG, "p2pConnect failure");
247 
248             return -1;
249         }
250 
251         Log.d(TAG, "p2pConnect succeeded - group not null");
252         return System.currentTimeMillis() - startTime;
253     }
254 
255     @Override
shutdown()256     public void shutdown() {
257         Log.d(TAG, "Wifi Direct shutdown called");
258     }
259 }
260