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