| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.net.wifi.p2p; |
| |
| import android.app.AlertDialog; |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.DialogInterface.OnClickListener; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.content.res.Resources; |
| import android.net.IConnectivityManager; |
| import android.net.ConnectivityManager; |
| import android.net.DhcpInfoInternal; |
| import android.net.DhcpStateMachine; |
| import android.net.InterfaceConfiguration; |
| import android.net.LinkAddress; |
| import android.net.LinkProperties; |
| import android.net.NetworkInfo; |
| import android.net.NetworkUtils; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.WifiMonitor; |
| import android.net.wifi.WifiNative; |
| import android.net.wifi.WifiStateMachine; |
| import android.net.wifi.WpsInfo; |
| import android.os.Binder; |
| import android.os.IBinder; |
| import android.os.INetworkManagementService; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Message; |
| import android.os.Messenger; |
| import android.os.ServiceManager; |
| import android.os.SystemProperties; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.util.Slog; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.WindowManager; |
| import android.widget.EditText; |
| import android.widget.TextView; |
| |
| import com.android.internal.R; |
| import com.android.internal.telephony.TelephonyIntents; |
| import com.android.internal.util.AsyncChannel; |
| import com.android.internal.util.Protocol; |
| import com.android.internal.util.State; |
| import com.android.internal.util.StateMachine; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.Collection; |
| |
| /** |
| * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications |
| * communicate with this service to issue device discovery and connectivity requests |
| * through the WifiP2pManager interface. The state machine communicates with the wifi |
| * driver through wpa_supplicant and handles the event responses through WifiMonitor. |
| * |
| * Note that the term Wifi when used without a p2p suffix refers to the client mode |
| * of Wifi operation |
| * @hide |
| */ |
| public class WifiP2pService extends IWifiP2pManager.Stub { |
| private static final String TAG = "WifiP2pService"; |
| private static final boolean DBG = true; |
| private static final String NETWORKTYPE = "WIFI_P2P"; |
| |
| private Context mContext; |
| private String mInterface; |
| private Notification mNotification; |
| |
| INetworkManagementService mNwService; |
| private DhcpStateMachine mDhcpStateMachine; |
| |
| //Tracked to notify the user about wifi client/hotspot being shut down |
| //during p2p bring up |
| private int mWifiState = WifiManager.WIFI_STATE_DISABLED; |
| private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED; |
| |
| private P2pStateMachine mP2pStateMachine; |
| private AsyncChannel mReplyChannel = new AsyncChannel(); |
| private AsyncChannel mWifiChannel; |
| |
| private static final Boolean JOIN_GROUP = true; |
| private static final Boolean FORM_GROUP = false; |
| |
| /* Two minutes comes from the wpa_supplicant setting */ |
| private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000; |
| private static int mGroupCreatingTimeoutIndex = 0; |
| |
| /** |
| * Delay between restarts upon failure to setup connection with supplicant |
| */ |
| private static final int P2P_RESTART_INTERVAL_MSECS = 5000; |
| |
| /** |
| * Number of times we attempt to restart p2p |
| */ |
| private static final int P2P_RESTART_TRIES = 5; |
| |
| private int mP2pRestartCount = 0; |
| |
| private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE; |
| |
| /* Message sent to WifiStateMachine to indicate p2p enable is pending */ |
| public static final int P2P_ENABLE_PENDING = BASE + 1; |
| /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */ |
| public static final int WIFI_ENABLE_PROCEED = BASE + 2; |
| |
| /* Delayed message to timeout group creation */ |
| public static final int GROUP_CREATING_TIMED_OUT = BASE + 3; |
| |
| /* User accepted to disable Wi-Fi in order to enable p2p */ |
| private static final int WIFI_DISABLE_USER_ACCEPT = BASE + 4; |
| /* User rejected to disable Wi-Fi in order to enable p2p */ |
| private static final int WIFI_DISABLE_USER_REJECT = BASE + 5; |
| |
| /* User accepted a peer request */ |
| private static final int PEER_CONNECTION_USER_ACCEPT = BASE + 6; |
| /* User rejected a peer request */ |
| private static final int PEER_CONNECTION_USER_REJECT = BASE + 7; |
| |
| /* Airplane mode changed */ |
| private static final int AIRPLANE_MODE_CHANGED = BASE + 8; |
| /* Emergency callback mode */ |
| private static final int EMERGENCY_CALLBACK_MODE = BASE + 9; |
| |
| private final boolean mP2pSupported; |
| |
| private WifiP2pDevice mThisDevice = new WifiP2pDevice(); |
| |
| /* When a group has been explicitly created by an app, we persist the group |
| * even after all clients have been disconnected until an explicit remove |
| * is invoked */ |
| private boolean mPersistGroup; |
| |
| private NetworkInfo mNetworkInfo; |
| |
| /* Is chosen as a unique range to avoid conflict with |
| the range defined in Tethering.java */ |
| private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"}; |
| private static final String SERVER_ADDRESS = "192.168.49.1"; |
| |
| public WifiP2pService(Context context) { |
| mContext = context; |
| |
| mInterface = SystemProperties.get("wifi.interface", "wlan0"); |
| mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, ""); |
| |
| mP2pSupported = mContext.getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_WIFI_DIRECT); |
| |
| mThisDevice.primaryDeviceType = mContext.getResources().getString( |
| com.android.internal.R.string.config_wifi_p2p_device_type); |
| mThisDevice.deviceName = getDefaultDeviceName(); |
| |
| mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported); |
| mP2pStateMachine.start(); |
| |
| // broadcasts |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); |
| filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); |
| filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); |
| filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); |
| mContext.registerReceiver(new WifiStateReceiver(), filter); |
| |
| } |
| |
| public void connectivityServiceReady() { |
| IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); |
| mNwService = INetworkManagementService.Stub.asInterface(b); |
| } |
| |
| private class WifiStateReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { |
| mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, |
| WifiManager.WIFI_STATE_DISABLED); |
| } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { |
| mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, |
| WifiManager.WIFI_AP_STATE_DISABLED); |
| } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { |
| mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED); |
| } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { |
| if (intent.getBooleanExtra("phoneinECMState", false) == true) { |
| mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE); |
| } |
| } |
| } |
| } |
| |
| private void enforceAccessPermission() { |
| mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, |
| "WifiP2pService"); |
| } |
| |
| private void enforceChangePermission() { |
| mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, |
| "WifiP2pService"); |
| } |
| |
| /* We use the 4 digits of the ANDROID_ID to have a friendly |
| * default that has low likelihood of collision with a peer */ |
| private String getDefaultDeviceName() { |
| String id = Settings.Secure.getString(mContext.getContentResolver(), |
| Settings.Secure.ANDROID_ID); |
| return "Android_" + id.substring(0,4); |
| } |
| |
| /** |
| * Get a reference to handler. This is used by a client to establish |
| * an AsyncChannel communication with WifiP2pService |
| */ |
| public Messenger getMessenger() { |
| enforceAccessPermission(); |
| enforceChangePermission(); |
| return new Messenger(mP2pStateMachine.getHandler()); |
| } |
| |
| @Override |
| protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) |
| != PackageManager.PERMISSION_GRANTED) { |
| pw.println("Permission Denial: can't dump WifiP2pService from from pid=" |
| + Binder.getCallingPid() |
| + ", uid=" + Binder.getCallingUid()); |
| return; |
| } |
| } |
| |
| |
| /** |
| * Handles interaction with WifiStateMachine |
| */ |
| private class P2pStateMachine extends StateMachine { |
| |
| private DefaultState mDefaultState = new DefaultState(); |
| private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState(); |
| private P2pDisablingState mP2pDisablingState = new P2pDisablingState(); |
| private P2pDisabledState mP2pDisabledState = new P2pDisabledState(); |
| private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState(); |
| private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState(); |
| private P2pEnablingState mP2pEnablingState = new P2pEnablingState(); |
| private P2pEnabledState mP2pEnabledState = new P2pEnabledState(); |
| // Inactive is when p2p is enabled with no connectivity |
| private InactiveState mInactiveState = new InactiveState(); |
| private GroupCreatingState mGroupCreatingState = new GroupCreatingState(); |
| private UserAuthorizingInvitationState mUserAuthorizingInvitationState |
| = new UserAuthorizingInvitationState(); |
| private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState(); |
| private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState(); |
| |
| private GroupCreatedState mGroupCreatedState = new GroupCreatedState(); |
| private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState(); |
| |
| private WifiMonitor mWifiMonitor = new WifiMonitor(this); |
| |
| private WifiP2pDeviceList mPeers = new WifiP2pDeviceList(); |
| private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo(); |
| private WifiP2pGroup mGroup; |
| |
| // Saved WifiP2pConfig for a peer connection |
| private WifiP2pConfig mSavedPeerConfig; |
| |
| // Saved WifiP2pGroup from invitation request |
| private WifiP2pGroup mSavedP2pGroup; |
| |
| P2pStateMachine(String name, boolean p2pSupported) { |
| super(name); |
| |
| addState(mDefaultState); |
| addState(mP2pNotSupportedState, mDefaultState); |
| addState(mP2pDisablingState, mDefaultState); |
| addState(mP2pDisabledState, mDefaultState); |
| addState(mWaitForUserActionState, mP2pDisabledState); |
| addState(mWaitForWifiDisableState, mP2pDisabledState); |
| addState(mP2pEnablingState, mDefaultState); |
| addState(mP2pEnabledState, mDefaultState); |
| addState(mInactiveState, mP2pEnabledState); |
| addState(mGroupCreatingState, mP2pEnabledState); |
| addState(mUserAuthorizingInvitationState, mGroupCreatingState); |
| addState(mProvisionDiscoveryState, mGroupCreatingState); |
| addState(mGroupNegotiationState, mGroupCreatingState); |
| addState(mGroupCreatedState, mP2pEnabledState); |
| addState(mUserAuthorizingJoinState, mGroupCreatedState); |
| |
| if (p2pSupported) { |
| setInitialState(mP2pDisabledState); |
| } else { |
| setInitialState(mP2pNotSupportedState); |
| } |
| } |
| |
| class DefaultState extends State { |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: |
| if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { |
| if (DBG) logd("Full connection with WifiStateMachine established"); |
| mWifiChannel = (AsyncChannel) message.obj; |
| } else { |
| loge("Full connection failure, error = " + message.arg1); |
| mWifiChannel = null; |
| } |
| break; |
| |
| case AsyncChannel.CMD_CHANNEL_DISCONNECTED: |
| if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) { |
| loge("Send failed, client connection lost"); |
| } else { |
| loge("Client connection lost with reason: " + message.arg1); |
| } |
| mWifiChannel = null; |
| break; |
| |
| case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: |
| AsyncChannel ac = new AsyncChannel(); |
| ac.connect(mContext, getHandler(), message.replyTo); |
| break; |
| case WifiStateMachine.WIFI_ENABLE_PENDING: |
| // Disable p2p operation before we can respond |
| sendMessage(WifiP2pManager.DISABLE_P2P); |
| deferMessage(message); |
| break; |
| case WifiP2pManager.ENABLE_P2P: |
| replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.DISABLE_P2P: |
| replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.DISCOVER_PEERS: |
| replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.CONNECT: |
| replyToMessage(message, WifiP2pManager.CONNECT_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.CANCEL_CONNECT: |
| replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.CREATE_GROUP: |
| replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.REMOVE_GROUP: |
| replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.REQUEST_PEERS: |
| replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers); |
| break; |
| case WifiP2pManager.REQUEST_CONNECTION_INFO: |
| replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo); |
| break; |
| case WifiP2pManager.REQUEST_GROUP_INFO: |
| replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup); |
| break; |
| case AIRPLANE_MODE_CHANGED: |
| if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P); |
| break; |
| case EMERGENCY_CALLBACK_MODE: |
| sendMessage(WifiP2pManager.DISABLE_P2P); |
| break; |
| // Ignore |
| case WifiMonitor.P2P_INVITATION_RESULT_EVENT: |
| case WIFI_DISABLE_USER_ACCEPT: |
| case WIFI_DISABLE_USER_REJECT: |
| case PEER_CONNECTION_USER_ACCEPT: |
| case PEER_CONNECTION_USER_REJECT: |
| case GROUP_CREATING_TIMED_OUT: |
| break; |
| default: |
| loge("Unhandled message " + message); |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class P2pNotSupportedState extends State { |
| @Override |
| public boolean processMessage(Message message) { |
| switch (message.what) { |
| // Allow Wi-Fi to proceed |
| case WifiStateMachine.WIFI_ENABLE_PENDING: |
| replyToMessage(message, WIFI_ENABLE_PROCEED); |
| break; |
| case WifiP2pManager.ENABLE_P2P: |
| replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| case WifiP2pManager.DISABLE_P2P: |
| replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| case WifiP2pManager.DISCOVER_PEERS: |
| replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| case WifiP2pManager.CONNECT: |
| replyToMessage(message, WifiP2pManager.CONNECT_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| case WifiP2pManager.CANCEL_CONNECT: |
| replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| case WifiP2pManager.CREATE_GROUP: |
| replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| case WifiP2pManager.REMOVE_GROUP: |
| replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, |
| WifiP2pManager.P2P_UNSUPPORTED); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class P2pDisablingState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| logd("stopping supplicant"); |
| if (!WifiNative.stopSupplicant()) { |
| loge("Failed to stop supplicant, issue kill"); |
| WifiNative.killSupplicant(); |
| } |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiMonitor.SUP_DISCONNECTION_EVENT: |
| logd("Supplicant connection lost"); |
| WifiNative.closeSupplicantConnection(); |
| transitionTo(mP2pDisabledState); |
| break; |
| case WifiP2pManager.ENABLE_P2P: |
| case WifiP2pManager.DISABLE_P2P: |
| deferMessage(message); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| |
| class P2pDisabledState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiP2pManager.ENABLE_P2P: |
| OnClickListener listener = new OnClickListener() { |
| @Override |
| public void onClick(DialogInterface dialog, int which) { |
| if (which == DialogInterface.BUTTON_POSITIVE) { |
| sendMessage(WIFI_DISABLE_USER_ACCEPT); |
| } else { |
| sendMessage(WIFI_DISABLE_USER_REJECT); |
| } |
| } |
| }; |
| |
| // Show a user request dialog if we know Wi-Fi client/hotspot is in operation |
| if (mWifiState != WifiManager.WIFI_STATE_DISABLED || |
| mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) { |
| Resources r = Resources.getSystem(); |
| AlertDialog dialog = new AlertDialog.Builder(mContext) |
| .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) |
| .setMessage(r.getString(R.string.wifi_p2p_turnon_message)) |
| .setPositiveButton(r.getString(R.string.ok), listener) |
| .setNegativeButton(r.getString(R.string.cancel), listener) |
| .create(); |
| dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); |
| dialog.show(); |
| transitionTo(mWaitForUserActionState); |
| } else { |
| mWifiChannel.sendMessage(P2P_ENABLE_PENDING); |
| transitionTo(mWaitForWifiDisableState); |
| } |
| replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED); |
| break; |
| case WifiP2pManager.DISABLE_P2P: |
| replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED); |
| break; |
| case WifiStateMachine.WIFI_ENABLE_PENDING: |
| replyToMessage(message, WIFI_ENABLE_PROCEED); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class WaitForUserActionState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WIFI_DISABLE_USER_ACCEPT: |
| mWifiChannel.sendMessage(P2P_ENABLE_PENDING); |
| transitionTo(mWaitForWifiDisableState); |
| break; |
| case WIFI_DISABLE_USER_REJECT: |
| logd("User rejected enabling p2p"); |
| sendP2pStateChangedBroadcast(false); |
| transitionTo(mP2pDisabledState); |
| break; |
| case WifiP2pManager.ENABLE_P2P: |
| case WifiP2pManager.DISABLE_P2P: |
| deferMessage(message); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class WaitForWifiDisableState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiStateMachine.P2P_ENABLE_PROCEED: |
| try { |
| mNwService.wifiFirmwareReload(mInterface, "P2P"); |
| } catch (Exception e) { |
| loge("Failed to reload p2p firmware " + e); |
| // continue |
| } |
| |
| //A runtime crash can leave the interface up and |
| //this affects p2p when supplicant starts up. |
| //Ensure interface is down before a supplicant start. |
| try { |
| mNwService.setInterfaceDown(mInterface); |
| } catch (Exception e) { |
| if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e); |
| } |
| |
| if (WifiNative.startP2pSupplicant()) { |
| mWifiMonitor.startMonitoring(); |
| transitionTo(mP2pEnablingState); |
| } else { |
| notifyP2pEnableFailure(); |
| transitionTo(mP2pDisabledState); |
| } |
| break; |
| case WifiP2pManager.ENABLE_P2P: |
| case WifiP2pManager.DISABLE_P2P: |
| deferMessage(message); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class P2pEnablingState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiMonitor.SUP_CONNECTION_EVENT: |
| logd("P2p start successful"); |
| transitionTo(mInactiveState); |
| break; |
| case WifiMonitor.SUP_DISCONNECTION_EVENT: |
| if (++mP2pRestartCount <= P2P_RESTART_TRIES) { |
| loge("Failed to start p2p, retry"); |
| WifiNative.killSupplicant(); |
| sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS); |
| } else { |
| loge("Failed " + mP2pRestartCount + " times to start p2p, quit "); |
| mP2pRestartCount = 0; |
| } |
| transitionTo(mP2pDisabledState); |
| break; |
| case WifiP2pManager.ENABLE_P2P: |
| case WifiP2pManager.DISABLE_P2P: |
| deferMessage(message); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class P2pEnabledState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| sendP2pStateChangedBroadcast(true); |
| mNetworkInfo.setIsAvailable(true); |
| initializeP2pSettings(); |
| showNotification(); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiP2pManager.ENABLE_P2P: |
| replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED); |
| break; |
| case WifiP2pManager.DISABLE_P2P: |
| if (mPeers.clear()) sendP2pPeersChangedBroadcast(); |
| replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED); |
| transitionTo(mP2pDisablingState); |
| break; |
| case WifiP2pManager.DISCOVER_PEERS: |
| int timeout = message.arg1; |
| if (WifiNative.p2pFind(timeout)) { |
| replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED); |
| } else { |
| replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, |
| WifiP2pManager.ERROR); |
| } |
| break; |
| case WifiMonitor.P2P_DEVICE_FOUND_EVENT: |
| WifiP2pDevice device = (WifiP2pDevice) message.obj; |
| if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break; |
| mPeers.update(device); |
| sendP2pPeersChangedBroadcast(); |
| break; |
| case WifiMonitor.P2P_DEVICE_LOST_EVENT: |
| device = (WifiP2pDevice) message.obj; |
| if (mPeers.remove(device)) sendP2pPeersChangedBroadcast(); |
| break; |
| case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant died */ |
| loge("Connection lost, restart p2p"); |
| WifiNative.killSupplicant(); |
| WifiNative.closeSupplicantConnection(); |
| if (mPeers.clear()) sendP2pPeersChangedBroadcast(); |
| transitionTo(mP2pDisabledState); |
| sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| @Override |
| public void exit() { |
| sendP2pStateChangedBroadcast(false); |
| mNetworkInfo.setIsAvailable(false); |
| clearNotification(); |
| } |
| } |
| |
| class InactiveState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| //Start listening every time we get inactive |
| WifiNative.p2pListen(); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiP2pManager.CONNECT: |
| if (DBG) logd(getName() + " sending connect"); |
| mSavedPeerConfig = (WifiP2pConfig) message.obj; |
| mPersistGroup = false; |
| int netId = configuredNetworkId(mSavedPeerConfig.deviceAddress); |
| if (netId >= 0) { |
| //TODO: if failure, remove config and do a regular p2pConnect() |
| WifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress); |
| } else { |
| //If peer is a GO, we do not need to send provisional discovery, |
| //the supplicant takes care of it. |
| if (isGroupOwner(mSavedPeerConfig.deviceAddress)) { |
| String pin = WifiNative.p2pConnect(mSavedPeerConfig, JOIN_GROUP); |
| try { |
| Integer.parseInt(pin); |
| notifyInvitationSent(pin, mSavedPeerConfig.deviceAddress); |
| } catch (NumberFormatException ignore) { |
| // do nothing if p2pConnect did not return a pin |
| } |
| transitionTo(mGroupNegotiationState); |
| } else { |
| transitionTo(mProvisionDiscoveryState); |
| } |
| } |
| updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); |
| sendP2pPeersChangedBroadcast(); |
| replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); |
| break; |
| case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT: |
| mSavedPeerConfig = (WifiP2pConfig) message.obj; |
| transitionTo(mUserAuthorizingInvitationState); |
| break; |
| case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT: |
| WifiP2pGroup group = (WifiP2pGroup) message.obj; |
| //TODO: fix p2p invitation to handle as a regular config |
| //and update mSavedPeerConfig |
| transitionTo(mUserAuthorizingInvitationState); |
| break; |
| case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: |
| case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: |
| case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: |
| WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; |
| mSavedPeerConfig = new WifiP2pConfig(); |
| mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress; |
| if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) { |
| mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; |
| if (DBG) logd("Keypad prov disc request"); |
| } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) { |
| mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; |
| mSavedPeerConfig.wps.pin = provDisc.pin; |
| if (DBG) logd("Display prov disc request"); |
| } else { |
| mSavedPeerConfig.wps.setup = WpsInfo.PBC; |
| if (DBG) logd("PBC prov disc request"); |
| } |
| transitionTo(mUserAuthorizingInvitationState); |
| break; |
| case WifiP2pManager.CREATE_GROUP: |
| mPersistGroup = true; |
| if (WifiNative.p2pGroupAdd()) { |
| replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED); |
| } else { |
| replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, |
| WifiP2pManager.ERROR); |
| } |
| transitionTo(mGroupNegotiationState); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class GroupCreatingState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT, |
| ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case GROUP_CREATING_TIMED_OUT: |
| if (mGroupCreatingTimeoutIndex == message.arg1) { |
| if (DBG) logd("Group negotiation timed out"); |
| updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.FAILED); |
| mSavedPeerConfig = null; |
| sendP2pPeersChangedBroadcast(); |
| transitionTo(mInactiveState); |
| } |
| break; |
| case WifiP2pManager.DISCOVER_PEERS: |
| /* Discovery will break negotiation */ |
| replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED, |
| WifiP2pManager.BUSY); |
| break; |
| case WifiP2pManager.CANCEL_CONNECT: |
| if (WifiNative.p2pCancelConnect()) { |
| replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED); |
| } else { |
| replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, |
| WifiP2pManager.ERROR); |
| } |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class UserAuthorizingInvitationState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| notifyInvitationReceived(); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case PEER_CONNECTION_USER_ACCEPT: |
| //TODO: handle persistence |
| if (isGroupOwner(mSavedPeerConfig.deviceAddress)) { |
| WifiNative.p2pConnect(mSavedPeerConfig, JOIN_GROUP); |
| } else { |
| WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); |
| } |
| updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED); |
| sendP2pPeersChangedBroadcast(); |
| transitionTo(mGroupNegotiationState); |
| break; |
| case PEER_CONNECTION_USER_REJECT: |
| if (DBG) logd("User rejected invitation " + mSavedPeerConfig); |
| mSavedPeerConfig = null; |
| transitionTo(mInactiveState); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| @Override |
| public void exit() { |
| //TODO: dismiss dialog if not already done |
| } |
| } |
| |
| class ProvisionDiscoveryState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| WifiNative.p2pProvisionDiscovery(mSavedPeerConfig); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| WifiP2pProvDiscEvent provDisc; |
| WifiP2pDevice device; |
| switch (message.what) { |
| case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT: |
| provDisc = (WifiP2pProvDiscEvent) message.obj; |
| device = provDisc.device; |
| if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; |
| |
| if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { |
| if (DBG) logd("Found a match " + mSavedPeerConfig); |
| WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); |
| transitionTo(mGroupNegotiationState); |
| } |
| break; |
| case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: |
| provDisc = (WifiP2pProvDiscEvent) message.obj; |
| device = provDisc.device; |
| if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; |
| |
| if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) { |
| if (DBG) logd("Found a match " + mSavedPeerConfig); |
| /* we already have the pin */ |
| if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) { |
| WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); |
| transitionTo(mGroupNegotiationState); |
| } else { |
| transitionTo(mUserAuthorizingInvitationState); |
| } |
| } |
| break; |
| case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: |
| provDisc = (WifiP2pProvDiscEvent) message.obj; |
| device = provDisc.device; |
| if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break; |
| |
| if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) { |
| if (DBG) logd("Found a match " + mSavedPeerConfig); |
| mSavedPeerConfig.wps.pin = provDisc.pin; |
| WifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); |
| notifyInvitationSent(provDisc.pin, device.deviceAddress); |
| transitionTo(mGroupNegotiationState); |
| } |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| class GroupNegotiationState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| // We ignore these right now, since we get a GROUP_STARTED notification |
| // afterwards |
| case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT: |
| case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT: |
| if (DBG) logd(getName() + " go success"); |
| break; |
| case WifiMonitor.P2P_GROUP_STARTED_EVENT: |
| mGroup = (WifiP2pGroup) message.obj; |
| if (DBG) logd(getName() + " group started"); |
| if (mGroup.isGroupOwner()) { |
| startDhcpServer(mGroup.getInterface()); |
| } else { |
| mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext, |
| P2pStateMachine.this, mGroup.getInterface()); |
| mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); |
| WifiP2pDevice groupOwner = mGroup.getOwner(); |
| updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED); |
| sendP2pPeersChangedBroadcast(); |
| } |
| mSavedPeerConfig = null; |
| transitionTo(mGroupCreatedState); |
| break; |
| case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT: |
| case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT: |
| if (DBG) logd(getName() + " go failure"); |
| updateDeviceStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.FAILED); |
| mSavedPeerConfig = null; |
| sendP2pPeersChangedBroadcast(); |
| transitionTo(mInactiveState); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| |
| |
| class GroupCreatedState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); |
| |
| updateThisDevice(WifiP2pDevice.CONNECTED); |
| |
| //DHCP server has already been started if I am a group owner |
| if (mGroup.isGroupOwner()) { |
| setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS); |
| sendP2pConnectionChangedBroadcast(); |
| } |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiMonitor.AP_STA_CONNECTED_EVENT: |
| //After a GO setup, STA connected event comes with interface address |
| String interfaceAddress = (String) message.obj; |
| String deviceAddress = getDeviceAddress(interfaceAddress); |
| if (deviceAddress != null) { |
| mGroup.addClient(deviceAddress); |
| updateDeviceStatus(deviceAddress, WifiP2pDevice.CONNECTED); |
| if (DBG) logd(getName() + " ap sta connected"); |
| sendP2pPeersChangedBroadcast(); |
| } else { |
| loge("Connect on unknown device address : " + interfaceAddress); |
| } |
| break; |
| case WifiMonitor.AP_STA_DISCONNECTED_EVENT: |
| interfaceAddress = (String) message.obj; |
| deviceAddress = getDeviceAddress(interfaceAddress); |
| if (deviceAddress != null) { |
| updateDeviceStatus(deviceAddress, WifiP2pDevice.AVAILABLE); |
| if (mGroup.removeClient(deviceAddress)) { |
| if (DBG) logd("Removed client " + deviceAddress); |
| if (!mPersistGroup && mGroup.isClientListEmpty()) { |
| Slog.d(TAG, "Client list empty, remove non-persistent p2p group"); |
| WifiNative.p2pGroupRemove(mGroup.getInterface()); |
| } |
| } else { |
| if (DBG) logd("Failed to remove client " + deviceAddress); |
| for (WifiP2pDevice c : mGroup.getClientList()) { |
| if (DBG) logd("client " + c.deviceAddress); |
| } |
| } |
| sendP2pPeersChangedBroadcast(); |
| if (DBG) loge(getName() + " ap sta disconnected"); |
| } else { |
| loge("Disconnect on unknown device address : " + interfaceAddress); |
| } |
| break; |
| case DhcpStateMachine.CMD_POST_DHCP_ACTION: |
| DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj; |
| if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS && |
| dhcpInfo != null) { |
| if (DBG) logd("DhcpInfo: " + dhcpInfo); |
| setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress); |
| sendP2pConnectionChangedBroadcast(); |
| } else { |
| WifiNative.p2pGroupRemove(mGroup.getInterface()); |
| } |
| break; |
| case WifiP2pManager.REMOVE_GROUP: |
| if (DBG) loge(getName() + " remove group"); |
| if (WifiNative.p2pGroupRemove(mGroup.getInterface())) { |
| replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED); |
| } else { |
| replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, |
| WifiP2pManager.ERROR); |
| } |
| break; |
| case WifiMonitor.P2P_GROUP_REMOVED_EVENT: |
| if (DBG) loge(getName() + " group removed"); |
| Collection <WifiP2pDevice> devices = mGroup.getClientList(); |
| boolean changed = false; |
| for (WifiP2pDevice d : mPeers.getDeviceList()) { |
| if (devices.contains(d) || mGroup.getOwner().equals(d)) { |
| d.status = WifiP2pDevice.AVAILABLE; |
| changed = true; |
| } |
| } |
| |
| if (mGroup.isGroupOwner()) { |
| stopDhcpServer(); |
| } else { |
| if (DBG) logd("stop DHCP client"); |
| mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP); |
| mDhcpStateMachine.quit(); |
| mDhcpStateMachine = null; |
| } |
| |
| mGroup = null; |
| if (changed) sendP2pPeersChangedBroadcast(); |
| transitionTo(mInactiveState); |
| break; |
| case WifiMonitor.P2P_DEVICE_LOST_EVENT: |
| WifiP2pDevice device = (WifiP2pDevice) message.obj; |
| if (device.equals(mGroup.getOwner())) { |
| logd("Lost the group owner, killing p2p connection"); |
| WifiNative.p2pGroupRemove(mGroup.getInterface()); |
| } else if (mGroup.removeClient(device)) { |
| if (!mPersistGroup && mGroup.isClientListEmpty()) { |
| Slog.d(TAG, "Client list empty, removing a non-persistent p2p group"); |
| WifiNative.p2pGroupRemove(mGroup.getInterface()); |
| } |
| } |
| return NOT_HANDLED; // Do the regular device lost handling |
| case WifiP2pManager.DISABLE_P2P: |
| sendMessage(WifiP2pManager.REMOVE_GROUP); |
| deferMessage(message); |
| break; |
| case WifiP2pManager.CONNECT: |
| WifiP2pConfig config = (WifiP2pConfig) message.obj; |
| logd("Inviting device : " + config.deviceAddress); |
| if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) { |
| updateDeviceStatus(config.deviceAddress, WifiP2pDevice.INVITED); |
| sendP2pPeersChangedBroadcast(); |
| replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); |
| } else { |
| replyToMessage(message, WifiP2pManager.CONNECT_FAILED, |
| WifiP2pManager.ERROR); |
| } |
| // TODO: figure out updating the status to declined when invitation is rejected |
| break; |
| case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: |
| case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: |
| case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: |
| WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; |
| mSavedPeerConfig = new WifiP2pConfig(); |
| mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress; |
| if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) { |
| mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; |
| } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) { |
| mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; |
| mSavedPeerConfig.wps.pin = provDisc.pin; |
| } else { |
| mSavedPeerConfig.wps.setup = WpsInfo.PBC; |
| } |
| transitionTo(mUserAuthorizingJoinState); |
| break; |
| case WifiMonitor.P2P_GROUP_STARTED_EVENT: |
| Slog.e(TAG, "Duplicate group creation event notice, ignore"); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| public void exit() { |
| updateThisDevice(WifiP2pDevice.AVAILABLE); |
| setWifiP2pInfoOnGroupTermination(); |
| mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); |
| sendP2pConnectionChangedBroadcast(); |
| } |
| } |
| |
| class UserAuthorizingJoinState extends State { |
| @Override |
| public void enter() { |
| if (DBG) logd(getName()); |
| notifyInvitationReceived(); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| if (DBG) logd(getName() + message.toString()); |
| switch (message.what) { |
| case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: |
| case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: |
| case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: |
| //Ignore more client requests |
| break; |
| case PEER_CONNECTION_USER_ACCEPT: |
| if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { |
| WifiNative.wpsPbc(); |
| } else { |
| WifiNative.wpsPin(mSavedPeerConfig.wps.pin); |
| } |
| mSavedPeerConfig = null; |
| transitionTo(mGroupCreatedState); |
| break; |
| case PEER_CONNECTION_USER_REJECT: |
| if (DBG) logd("User rejected incoming request"); |
| mSavedPeerConfig = null; |
| transitionTo(mGroupCreatedState); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| @Override |
| public void exit() { |
| //TODO: dismiss dialog if not already done |
| } |
| } |
| |
| private void sendP2pStateChangedBroadcast(boolean enabled) { |
| final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| if (enabled) { |
| intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE, |
| WifiP2pManager.WIFI_P2P_STATE_ENABLED); |
| } else { |
| intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE, |
| WifiP2pManager.WIFI_P2P_STATE_DISABLED); |
| } |
| mContext.sendStickyBroadcast(intent); |
| } |
| |
| private void sendThisDeviceChangedBroadcast() { |
| final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice)); |
| mContext.sendStickyBroadcast(intent); |
| } |
| |
| private void sendP2pPeersChangedBroadcast() { |
| final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| mContext.sendBroadcast(intent); |
| } |
| |
| private void sendP2pConnectionChangedBroadcast() { |
| if (DBG) logd("sending p2p connection changed broadcast"); |
| Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
| | Intent.FLAG_RECEIVER_REPLACE_PENDING); |
| intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo)); |
| intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo)); |
| mContext.sendStickyBroadcast(intent); |
| } |
| |
| private void startDhcpServer(String intf) { |
| InterfaceConfiguration ifcg = null; |
| try { |
| ifcg = mNwService.getInterfaceConfig(intf); |
| ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress( |
| SERVER_ADDRESS), 24)); |
| ifcg.setInterfaceUp(); |
| mNwService.setInterfaceConfig(intf, ifcg); |
| /* This starts the dnsmasq server */ |
| mNwService.startTethering(DHCP_RANGE); |
| } catch (Exception e) { |
| loge("Error configuring interface " + intf + ", :" + e); |
| return; |
| } |
| |
| logd("Started Dhcp server on " + intf); |
| } |
| |
| private void stopDhcpServer() { |
| try { |
| mNwService.stopTethering(); |
| } catch (Exception e) { |
| loge("Error stopping Dhcp server" + e); |
| return; |
| } |
| |
| logd("Stopped Dhcp server"); |
| } |
| |
| private void notifyP2pEnableFailure() { |
| Resources r = Resources.getSystem(); |
| AlertDialog dialog = new AlertDialog.Builder(mContext) |
| .setTitle(r.getString(R.string.wifi_p2p_dialog_title)) |
| .setMessage(r.getString(R.string.wifi_p2p_failed_message)) |
| .setPositiveButton(r.getString(R.string.ok), null) |
| .create(); |
| dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); |
| dialog.show(); |
| } |
| |
| private void addRowToDialog(ViewGroup group, int stringId, String value) { |
| Resources r = Resources.getSystem(); |
| View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row, |
| group, false); |
| ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId)); |
| ((TextView) row.findViewById(R.id.value)).setText(value); |
| group.addView(row); |
| } |
| |
| private void notifyInvitationSent(String pin, String peerAddress) { |
| Resources r = Resources.getSystem(); |
| |
| final View textEntryView = LayoutInflater.from(mContext) |
| .inflate(R.layout.wifi_p2p_dialog, null); |
| |
| ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info); |
| addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress)); |
| addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin); |
| |
| AlertDialog dialog = new AlertDialog.Builder(mContext) |
| .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title)) |
| .setView(textEntryView) |
| .setPositiveButton(r.getString(R.string.ok), null) |
| .create(); |
| dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); |
| dialog.show(); |
| } |
| |
| private void notifyInvitationReceived() { |
| Resources r = Resources.getSystem(); |
| final WpsInfo wps = mSavedPeerConfig.wps; |
| final View textEntryView = LayoutInflater.from(mContext) |
| .inflate(R.layout.wifi_p2p_dialog, null); |
| |
| ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info); |
| addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName( |
| mSavedPeerConfig.deviceAddress)); |
| |
| final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin); |
| |
| AlertDialog dialog = new AlertDialog.Builder(mContext) |
| .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title)) |
| .setView(textEntryView) |
| .setPositiveButton(r.getString(R.string.accept), new OnClickListener() { |
| public void onClick(DialogInterface dialog, int which) { |
| if (wps.setup == WpsInfo.KEYPAD) { |
| mSavedPeerConfig.wps.pin = pin.getText().toString(); |
| } |
| if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig); |
| sendMessage(PEER_CONNECTION_USER_ACCEPT); |
| } |
| }) |
| .setNegativeButton(r.getString(R.string.decline), new OnClickListener() { |
| @Override |
| public void onClick(DialogInterface dialog, int which) { |
| if (DBG) logd(getName() + " ignore connect"); |
| sendMessage(PEER_CONNECTION_USER_REJECT); |
| } |
| }) |
| .create(); |
| |
| //make the enter pin area or the display pin area visible |
| switch (wps.setup) { |
| case WpsInfo.KEYPAD: |
| if (DBG) logd("Enter pin section visible"); |
| textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE); |
| break; |
| case WpsInfo.DISPLAY: |
| if (DBG) logd("Shown pin section visible"); |
| addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin); |
| break; |
| default: |
| break; |
| } |
| |
| dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); |
| dialog.show(); |
| } |
| |
| private void updateDeviceStatus(String deviceAddress, int status) { |
| for (WifiP2pDevice d : mPeers.getDeviceList()) { |
| if (d.deviceAddress.equals(deviceAddress)) { |
| d.status = status; |
| } |
| } |
| } |
| |
| private boolean isGroupOwner(String deviceAddress) { |
| for (WifiP2pDevice d : mPeers.getDeviceList()) { |
| if (d.deviceAddress.equals(deviceAddress)) { |
| return d.isGroupOwner(); |
| } |
| } |
| return false; |
| } |
| |
| //TODO: implement when wpa_supplicant is fixed |
| private int configuredNetworkId(String deviceAddress) { |
| return -1; |
| } |
| |
| private void setWifiP2pInfoOnGroupFormation(String serverAddress) { |
| mWifiP2pInfo.groupFormed = true; |
| mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner(); |
| mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress); |
| } |
| |
| private void setWifiP2pInfoOnGroupTermination() { |
| mWifiP2pInfo.groupFormed = false; |
| mWifiP2pInfo.isGroupOwner = false; |
| mWifiP2pInfo.groupOwnerAddress = null; |
| } |
| |
| private String getDeviceName(String deviceAddress) { |
| for (WifiP2pDevice d : mPeers.getDeviceList()) { |
| if (d.deviceAddress.equals(deviceAddress)) { |
| return d.deviceName; |
| } |
| } |
| //Treat the address as name if there is no match |
| return deviceAddress; |
| } |
| |
| private String getDeviceAddress(String interfaceAddress) { |
| for (WifiP2pDevice d : mPeers.getDeviceList()) { |
| if (interfaceAddress.equals(WifiNative.p2pGetInterfaceAddress(d.deviceAddress))) { |
| return d.deviceAddress; |
| } |
| } |
| return null; |
| } |
| |
| private void initializeP2pSettings() { |
| WifiNative.setPersistentReconnect(true); |
| WifiNative.setDeviceName(mThisDevice.deviceName); |
| WifiNative.setDeviceType(mThisDevice.primaryDeviceType); |
| //The supplicant default is to support everything, but a bug necessitates |
| //the framework to specify this explicitly |
| WifiNative.setConfigMethods("keypad display push_button"); |
| |
| mThisDevice.deviceAddress = WifiNative.p2pGetDeviceAddress(); |
| updateThisDevice(WifiP2pDevice.AVAILABLE); |
| if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress); |
| } |
| |
| private void updateThisDevice(int status) { |
| mThisDevice.status = status; |
| sendThisDeviceChangedBroadcast(); |
| } |
| |
| //State machine initiated requests can have replyTo set to null indicating |
| //there are no recepients, we ignore those reply actions |
| private void replyToMessage(Message msg, int what) { |
| if (msg.replyTo == null) return; |
| Message dstMsg = obtainMessage(msg); |
| dstMsg.what = what; |
| mReplyChannel.replyToMessage(msg, dstMsg); |
| } |
| |
| private void replyToMessage(Message msg, int what, int arg1) { |
| if (msg.replyTo == null) return; |
| Message dstMsg = obtainMessage(msg); |
| dstMsg.what = what; |
| dstMsg.arg1 = arg1; |
| mReplyChannel.replyToMessage(msg, dstMsg); |
| } |
| |
| private void replyToMessage(Message msg, int what, Object obj) { |
| if (msg.replyTo == null) return; |
| Message dstMsg = obtainMessage(msg); |
| dstMsg.what = what; |
| dstMsg.obj = obj; |
| mReplyChannel.replyToMessage(msg, dstMsg); |
| } |
| |
| /* arg2 on the source message has a hash code that needs to be retained in replies |
| * see WifiP2pManager for details */ |
| private Message obtainMessage(Message srcMsg) { |
| Message msg = Message.obtain(); |
| msg.arg2 = srcMsg.arg2; |
| return msg; |
| } |
| |
| private void logd(String s) { |
| Slog.d(TAG, s); |
| } |
| |
| private void loge(String s) { |
| Slog.e(TAG, s); |
| } |
| |
| private void showNotification() { |
| NotificationManager notificationManager = |
| (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); |
| if (notificationManager == null || mNotification != null) { |
| return; |
| } |
| |
| Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); |
| intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); |
| |
| PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); |
| |
| Resources r = Resources.getSystem(); |
| CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title); |
| CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message); |
| |
| mNotification = new Notification(); |
| mNotification.when = 0; |
| //TODO: might change to be a seperate icon |
| mNotification.icon = R.drawable.stat_sys_tether_wifi; |
| mNotification.defaults &= ~Notification.DEFAULT_SOUND; |
| mNotification.flags = Notification.FLAG_ONGOING_EVENT; |
| mNotification.tickerText = title; |
| mNotification.setLatestEventInfo(mContext, title, message, pi); |
| |
| notificationManager.notify(mNotification.icon, mNotification); |
| } |
| |
| private void clearNotification() { |
| NotificationManager notificationManager = |
| (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); |
| if (notificationManager != null && mNotification != null) { |
| notificationManager.cancel(mNotification.icon); |
| mNotification = null; |
| } |
| } |
| |
| private boolean isAirplaneSensitive() { |
| String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), |
| Settings.System.AIRPLANE_MODE_RADIOS); |
| return airplaneModeRadios == null |
| || airplaneModeRadios.contains(Settings.System.RADIO_WIFI); |
| } |
| |
| private boolean isAirplaneModeOn() { |
| return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(), |
| Settings.System.AIRPLANE_MODE_ON, 0) == 1; |
| } |
| |
| } |
| } |