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