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