Home | History | Annotate | Download | only in p2p
      1 /*
      2  * Copyright (C) 2011 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 android.net.wifi.p2p;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.Notification;
     21 import android.app.NotificationManager;
     22 import android.app.PendingIntent;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.DialogInterface.OnClickListener;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.PackageManager;
     30 import android.content.res.Resources;
     31 import android.net.IConnectivityManager;
     32 import android.net.ConnectivityManager;
     33 import android.net.DhcpInfoInternal;
     34 import android.net.DhcpStateMachine;
     35 import android.net.InterfaceConfiguration;
     36 import android.net.LinkAddress;
     37 import android.net.LinkProperties;
     38 import android.net.NetworkInfo;
     39 import android.net.NetworkUtils;
     40 import android.net.wifi.WifiManager;
     41 import android.net.wifi.WifiMonitor;
     42 import android.net.wifi.WifiNative;
     43 import android.net.wifi.WifiStateMachine;
     44 import android.net.wifi.WpsInfo;
     45 import android.os.Binder;
     46 import android.os.IBinder;
     47 import android.os.INetworkManagementService;
     48 import android.os.Handler;
     49 import android.os.HandlerThread;
     50 import android.os.Message;
     51 import android.os.Messenger;
     52 import android.os.ServiceManager;
     53 import android.os.SystemProperties;
     54 import android.provider.Settings;
     55 import android.util.Slog;
     56 import android.view.LayoutInflater;
     57 import android.view.View;
     58 import android.view.WindowManager;
     59 import android.widget.EditText;
     60 
     61 import com.android.internal.R;
     62 import com.android.internal.telephony.TelephonyIntents;
     63 import com.android.internal.util.AsyncChannel;
     64 import com.android.internal.util.Protocol;
     65 import com.android.internal.util.State;
     66 import com.android.internal.util.StateMachine;
     67 
     68 import java.io.FileDescriptor;
     69 import java.io.PrintWriter;
     70 import java.util.Collection;
     71 
     72 /**
     73  * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications
     74  * communicate with this service to issue device discovery and connectivity requests
     75  * through the WifiP2pManager interface. The state machine communicates with the wifi
     76  * driver through wpa_supplicant and handles the event responses through WifiMonitor.
     77  *
     78  * Note that the term Wifi when used without a p2p suffix refers to the client mode
     79  * of Wifi operation
     80  * @hide
     81  */
     82 public class WifiP2pService extends IWifiP2pManager.Stub {
     83     private static final String TAG = "WifiP2pService";
     84     private static final boolean DBG = false;
     85     private static final String NETWORKTYPE = "WIFI_P2P";
     86 
     87     private Context mContext;
     88     private String mInterface;
     89     private Notification mNotification;
     90 
     91     INetworkManagementService mNwService;
     92     private DhcpStateMachine mDhcpStateMachine;
     93 
     94     //Tracked to notify the user about wifi client/hotspot being shut down
     95     //during p2p bring up
     96     private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
     97     private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
     98 
     99     private P2pStateMachine mP2pStateMachine;
    100     private AsyncChannel mReplyChannel = new AsyncChannel();
    101     private AsyncChannel mWifiChannel;
    102 
    103     /* Two minutes comes from the wpa_supplicant setting */
    104     private static final int GROUP_NEGOTIATION_WAIT_TIME_MS = 120 * 1000;
    105     private static int mGroupNegotiationTimeoutIndex = 0;
    106 
    107     /**
    108      * Delay between restarts upon failure to setup connection with supplicant
    109      */
    110     private static final int P2P_RESTART_INTERVAL_MSECS = 5000;
    111 
    112     /**
    113      * Number of times we attempt to restart p2p
    114      */
    115     private static final int P2P_RESTART_TRIES = 5;
    116 
    117     private int mP2pRestartCount = 0;
    118 
    119     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
    120 
    121     /* Message sent to WifiStateMachine to indicate p2p enable is pending */
    122     public static final int P2P_ENABLE_PENDING              =   BASE + 1;
    123     /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
    124     public static final int WIFI_ENABLE_PROCEED             =   BASE + 2;
    125 
    126     /* Delayed message to timeout of group negotiation */
    127     public static final int GROUP_NEGOTIATION_TIMED_OUT     =   BASE + 3;
    128 
    129     /* User accepted to disable Wi-Fi in order to enable p2p */
    130     private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 4;
    131     /* User rejected to disable Wi-Fi in order to enable p2p */
    132     private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
    133 
    134     /* User accepted a group negotiation request */
    135     private static final int GROUP_NEGOTIATION_USER_ACCEPT  =   BASE + 6;
    136     /* User rejected a group negotiation request */
    137     private static final int GROUP_NEGOTIATION_USER_REJECT  =   BASE + 7;
    138 
    139     /* User accepted a group invitation request */
    140     private static final int GROUP_INVITATION_USER_ACCEPT   =   BASE + 8;
    141     /* User rejected a group invitation request */
    142     private static final int GROUP_INVITATION_USER_REJECT   =   BASE + 9;
    143 
    144     /* Airplane mode changed */
    145     private static final int AIRPLANE_MODE_CHANGED          =   BASE + 10;
    146     /* Emergency callback mode */
    147     private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 11;
    148     private static final int WPS_PBC                        =   BASE + 12;
    149     private static final int WPS_PIN                        =   BASE + 13;
    150 
    151     private final boolean mP2pSupported;
    152 
    153     private WifiP2pDevice mThisDevice = new WifiP2pDevice();
    154 
    155     /* When a group has been explicitly created by an app, we persist the group
    156      * even after all clients have been disconnected until an explicit remove
    157      * is invoked */
    158     private boolean mPersistGroup;
    159 
    160     private NetworkInfo mNetworkInfo;
    161 
    162     /* Is chosen as a unique range to avoid conflict with
    163        the range defined in Tethering.java */
    164     private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
    165     private static final String SERVER_ADDRESS = "192.168.49.1";
    166 
    167     public WifiP2pService(Context context) {
    168         mContext = context;
    169 
    170         mInterface = SystemProperties.get("wifi.interface", "wlan0");
    171         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
    172 
    173         mP2pSupported = mContext.getPackageManager().hasSystemFeature(
    174                 PackageManager.FEATURE_WIFI_DIRECT);
    175 
    176         mThisDevice.primaryDeviceType = mContext.getResources().getString(
    177                 com.android.internal.R.string.config_wifi_p2p_device_type);
    178         mThisDevice.deviceName = getDefaultDeviceName();
    179 
    180         mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
    181         mP2pStateMachine.start();
    182 
    183         // broadcasts
    184         IntentFilter filter = new IntentFilter();
    185         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    186         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    187         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
    188         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
    189         mContext.registerReceiver(new WifiStateReceiver(), filter);
    190 
    191     }
    192 
    193     public void connectivityServiceReady() {
    194         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    195         mNwService = INetworkManagementService.Stub.asInterface(b);
    196     }
    197 
    198     private class WifiStateReceiver extends BroadcastReceiver {
    199         @Override
    200         public void onReceive(Context context, Intent intent) {
    201             String action = intent.getAction();
    202             if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
    203                 mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    204                         WifiManager.WIFI_STATE_DISABLED);
    205             } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
    206                 mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
    207                         WifiManager.WIFI_AP_STATE_DISABLED);
    208             } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
    209                 mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
    210             } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
    211                 if (intent.getBooleanExtra("phoneinECMState", false) == true) {
    212                     mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
    213                 }
    214             }
    215         }
    216     }
    217 
    218     private void enforceAccessPermission() {
    219         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
    220                 "WifiP2pService");
    221     }
    222 
    223     private void enforceChangePermission() {
    224         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
    225                 "WifiP2pService");
    226     }
    227 
    228     /* We use the 4 digits of the ANDROID_ID to have a friendly
    229      * default that has low likelihood of collision with a peer */
    230     private String getDefaultDeviceName() {
    231         String id = Settings.Secure.getString(mContext.getContentResolver(),
    232                     Settings.Secure.ANDROID_ID);
    233         return "Android_" + id.substring(0,4);
    234     }
    235 
    236     /**
    237      * Get a reference to handler. This is used by a client to establish
    238      * an AsyncChannel communication with WifiP2pService
    239      */
    240     public Messenger getMessenger() {
    241         enforceAccessPermission();
    242         enforceChangePermission();
    243         return new Messenger(mP2pStateMachine.getHandler());
    244     }
    245 
    246     @Override
    247     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    248         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    249                 != PackageManager.PERMISSION_GRANTED) {
    250             pw.println("Permission Denial: can't dump WifiP2pService from from pid="
    251                     + Binder.getCallingPid()
    252                     + ", uid=" + Binder.getCallingUid());
    253             return;
    254         }
    255     }
    256 
    257 
    258     /**
    259      * Handles interaction with WifiStateMachine
    260      */
    261     private class P2pStateMachine extends StateMachine {
    262 
    263         private DefaultState mDefaultState = new DefaultState();
    264         private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
    265         private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
    266         private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
    267         private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
    268         private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
    269         private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
    270         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
    271         // Inactive is when p2p is enabled with no connectivity
    272         private InactiveState mInactiveState = new InactiveState();
    273         private UserAuthorizingGroupNegotiationState mUserAuthorizingGroupNegotiationState
    274                 = new UserAuthorizingGroupNegotiationState();
    275         private UserAuthorizingGroupInvitationState mUserAuthorizingGroupInvitationState
    276                 = new UserAuthorizingGroupInvitationState();
    277         private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
    278         private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
    279 
    280         private WifiMonitor mWifiMonitor = new WifiMonitor(this);
    281 
    282         private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
    283         private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
    284         private WifiP2pGroup mGroup;
    285 
    286         // Saved WifiP2pConfig from GO negotiation request
    287         private WifiP2pConfig mSavedGoNegotiationConfig;
    288 
    289         // Saved WifiP2pConfig from connect request
    290         private WifiP2pConfig mSavedConnectConfig;
    291 
    292         // Saved WifiP2pGroup from invitation request
    293         private WifiP2pGroup mSavedP2pGroup;
    294 
    295         P2pStateMachine(String name, boolean p2pSupported) {
    296             super(name);
    297 
    298             addState(mDefaultState);
    299                 addState(mP2pNotSupportedState, mDefaultState);
    300                 addState(mP2pDisablingState, mDefaultState);
    301                 addState(mP2pDisabledState, mDefaultState);
    302                     addState(mWaitForUserActionState, mP2pDisabledState);
    303                     addState(mWaitForWifiDisableState, mP2pDisabledState);
    304                 addState(mP2pEnablingState, mDefaultState);
    305                 addState(mP2pEnabledState, mDefaultState);
    306                     addState(mInactiveState, mP2pEnabledState);
    307                         addState(mUserAuthorizingGroupNegotiationState, mInactiveState);
    308                         addState(mUserAuthorizingGroupInvitationState, mInactiveState);
    309                     addState(mGroupNegotiationState, mP2pEnabledState);
    310                     addState(mGroupCreatedState, mP2pEnabledState);
    311 
    312             if (p2pSupported) {
    313                 setInitialState(mP2pDisabledState);
    314             } else {
    315                 setInitialState(mP2pNotSupportedState);
    316             }
    317         }
    318 
    319     class DefaultState extends State {
    320         @Override
    321         public boolean processMessage(Message message) {
    322             if (DBG) logd(getName() + message.toString());
    323             switch (message.what) {
    324                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
    325                     if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    326                         if (DBG) logd("Full connection with WifiStateMachine established");
    327                         mWifiChannel = (AsyncChannel) message.obj;
    328                     } else {
    329                         loge("Full connection failure, error = " + message.arg1);
    330                         mWifiChannel = null;
    331                     }
    332                     break;
    333 
    334                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
    335                     if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
    336                         loge("Send failed, client connection lost");
    337                     } else {
    338                         loge("Client connection lost with reason: " + message.arg1);
    339                     }
    340                     mWifiChannel = null;
    341                     break;
    342 
    343                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
    344                     AsyncChannel ac = new AsyncChannel();
    345                     ac.connect(mContext, getHandler(), message.replyTo);
    346                     break;
    347                 case WifiStateMachine.WIFI_ENABLE_PENDING:
    348                     // Disable p2p operation before we can respond
    349                     sendMessage(WifiP2pManager.DISABLE_P2P);
    350                     deferMessage(message);
    351                     break;
    352                 case WifiP2pManager.ENABLE_P2P:
    353                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
    354                             WifiP2pManager.BUSY);
    355                     break;
    356                 case WifiP2pManager.DISABLE_P2P:
    357                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
    358                             WifiP2pManager.BUSY);
    359                     break;
    360                 case WifiP2pManager.DISCOVER_PEERS:
    361                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    362                             WifiP2pManager.BUSY);
    363                     break;
    364                 case WifiP2pManager.CONNECT:
    365                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
    366                             WifiP2pManager.BUSY);
    367                     break;
    368                 case WifiP2pManager.CANCEL_CONNECT:
    369                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
    370                             WifiP2pManager.BUSY);
    371                     break;
    372                 case WifiP2pManager.CREATE_GROUP:
    373                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
    374                             WifiP2pManager.BUSY);
    375                     break;
    376                 case WifiP2pManager.REMOVE_GROUP:
    377                     replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
    378                             WifiP2pManager.BUSY);
    379                     break;
    380                 case WifiP2pManager.REQUEST_PEERS:
    381                     replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
    382                     break;
    383                 case WifiP2pManager.REQUEST_CONNECTION_INFO:
    384                     replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo);
    385                     break;
    386                 case WifiP2pManager.REQUEST_GROUP_INFO:
    387                     replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
    388                     break;
    389                 case AIRPLANE_MODE_CHANGED:
    390                     if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
    391                     break;
    392                 case EMERGENCY_CALLBACK_MODE:
    393                     sendMessage(WifiP2pManager.DISABLE_P2P);
    394                     break;
    395                     // Ignore
    396                 case WIFI_DISABLE_USER_ACCEPT:
    397                 case WIFI_DISABLE_USER_REJECT:
    398                 case GROUP_NEGOTIATION_USER_ACCEPT:
    399                 case GROUP_NEGOTIATION_USER_REJECT:
    400                 case GROUP_INVITATION_USER_ACCEPT:
    401                 case GROUP_INVITATION_USER_REJECT:
    402                 case GROUP_NEGOTIATION_TIMED_OUT:
    403                     break;
    404                 default:
    405                     loge("Unhandled message " + message);
    406                     return NOT_HANDLED;
    407             }
    408             return HANDLED;
    409         }
    410     }
    411 
    412     class P2pNotSupportedState extends State {
    413         @Override
    414         public boolean processMessage(Message message) {
    415             switch (message.what) {
    416                 // Allow Wi-Fi to proceed
    417                 case WifiStateMachine.WIFI_ENABLE_PENDING:
    418                     replyToMessage(message, WIFI_ENABLE_PROCEED);
    419                     break;
    420                 case WifiP2pManager.ENABLE_P2P:
    421                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
    422                             WifiP2pManager.P2P_UNSUPPORTED);
    423                     break;
    424                 case WifiP2pManager.DISABLE_P2P:
    425                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
    426                             WifiP2pManager.P2P_UNSUPPORTED);
    427                     break;
    428                 case WifiP2pManager.DISCOVER_PEERS:
    429                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    430                             WifiP2pManager.P2P_UNSUPPORTED);
    431                     break;
    432                 case WifiP2pManager.CONNECT:
    433                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
    434                             WifiP2pManager.P2P_UNSUPPORTED);
    435                     break;
    436                 case WifiP2pManager.CANCEL_CONNECT:
    437                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
    438                             WifiP2pManager.P2P_UNSUPPORTED);
    439                     break;
    440                 case WifiP2pManager.CREATE_GROUP:
    441                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
    442                             WifiP2pManager.P2P_UNSUPPORTED);
    443                     break;
    444                 case WifiP2pManager.REMOVE_GROUP:
    445                     replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
    446                             WifiP2pManager.P2P_UNSUPPORTED);
    447                     break;
    448                default:
    449                     return NOT_HANDLED;
    450             }
    451             return HANDLED;
    452         }
    453     }
    454 
    455     class P2pDisablingState extends State {
    456         @Override
    457         public void enter() {
    458             if (DBG) logd(getName());
    459             logd("stopping supplicant");
    460             if (!WifiNative.stopSupplicant()) {
    461                 loge("Failed to stop supplicant, issue kill");
    462                 WifiNative.killSupplicant();
    463             }
    464         }
    465 
    466         @Override
    467         public boolean processMessage(Message message) {
    468             if (DBG) logd(getName() + message.toString());
    469             switch (message.what) {
    470                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
    471                     logd("Supplicant connection lost");
    472                     WifiNative.closeSupplicantConnection();
    473                     transitionTo(mP2pDisabledState);
    474                     break;
    475                 case WifiP2pManager.ENABLE_P2P:
    476                 case WifiP2pManager.DISABLE_P2P:
    477                     deferMessage(message);
    478                     break;
    479                 default:
    480                     return NOT_HANDLED;
    481             }
    482             return HANDLED;
    483         }
    484     }
    485 
    486 
    487     class P2pDisabledState extends State {
    488        @Override
    489         public void enter() {
    490             if (DBG) logd(getName());
    491         }
    492 
    493         @Override
    494         public boolean processMessage(Message message) {
    495             if (DBG) logd(getName() + message.toString());
    496             switch (message.what) {
    497                 case WifiP2pManager.ENABLE_P2P:
    498                     OnClickListener listener = new OnClickListener() {
    499                         @Override
    500                         public void onClick(DialogInterface dialog, int which) {
    501                             if (which == DialogInterface.BUTTON_POSITIVE) {
    502                                 sendMessage(WIFI_DISABLE_USER_ACCEPT);
    503                             } else {
    504                                 sendMessage(WIFI_DISABLE_USER_REJECT);
    505                             }
    506                         }
    507                     };
    508 
    509                     // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
    510                     if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
    511                             mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
    512                         Resources r = Resources.getSystem();
    513                         AlertDialog dialog = new AlertDialog.Builder(mContext)
    514                             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
    515                             .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
    516                             .setPositiveButton(r.getString(R.string.ok), listener)
    517                             .setNegativeButton(r.getString(R.string.cancel), listener)
    518                             .create();
    519                         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    520                         dialog.show();
    521                         transitionTo(mWaitForUserActionState);
    522                     } else {
    523                         mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
    524                         transitionTo(mWaitForWifiDisableState);
    525                     }
    526                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
    527                     break;
    528                 case WifiP2pManager.DISABLE_P2P:
    529                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
    530                     break;
    531                 case WifiStateMachine.WIFI_ENABLE_PENDING:
    532                     replyToMessage(message, WIFI_ENABLE_PROCEED);
    533                     break;
    534                 default:
    535                     return NOT_HANDLED;
    536             }
    537             return HANDLED;
    538         }
    539     }
    540 
    541     class WaitForUserActionState extends State {
    542         @Override
    543         public void enter() {
    544             if (DBG) logd(getName());
    545         }
    546 
    547         @Override
    548         public boolean processMessage(Message message) {
    549             if (DBG) logd(getName() + message.toString());
    550             switch (message.what) {
    551                 case WIFI_DISABLE_USER_ACCEPT:
    552                     mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
    553                     transitionTo(mWaitForWifiDisableState);
    554                     break;
    555                 case WIFI_DISABLE_USER_REJECT:
    556                     logd("User rejected enabling p2p");
    557                     sendP2pStateChangedBroadcast(false);
    558                     transitionTo(mP2pDisabledState);
    559                     break;
    560                 case WifiP2pManager.ENABLE_P2P:
    561                 case WifiP2pManager.DISABLE_P2P:
    562                     deferMessage(message);
    563                     break;
    564                 default:
    565                     return NOT_HANDLED;
    566             }
    567             return HANDLED;
    568         }
    569     }
    570 
    571     class WaitForWifiDisableState extends State {
    572         @Override
    573         public void enter() {
    574             if (DBG) logd(getName());
    575         }
    576 
    577         @Override
    578         public boolean processMessage(Message message) {
    579             if (DBG) logd(getName() + message.toString());
    580             switch (message.what) {
    581                 case WifiStateMachine.P2P_ENABLE_PROCEED:
    582                     try {
    583                         mNwService.wifiFirmwareReload(mInterface, "P2P");
    584                     } catch (Exception e) {
    585                         loge("Failed to reload p2p firmware " + e);
    586                         // continue
    587                     }
    588 
    589                     //A runtime crash can leave the interface up and
    590                     //this affects p2p when supplicant starts up.
    591                     //Ensure interface is down before a supplicant start.
    592                     try {
    593                         mNwService.setInterfaceDown(mInterface);
    594                     } catch (Exception e) {
    595                         if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
    596                     }
    597 
    598                     if (WifiNative.startP2pSupplicant()) {
    599                         mWifiMonitor.startMonitoring();
    600                         transitionTo(mP2pEnablingState);
    601                     } else {
    602                         notifyP2pEnableFailure();
    603                         transitionTo(mP2pDisabledState);
    604                     }
    605                     break;
    606                 case WifiP2pManager.ENABLE_P2P:
    607                 case WifiP2pManager.DISABLE_P2P:
    608                     deferMessage(message);
    609                     break;
    610                 default:
    611                     return NOT_HANDLED;
    612             }
    613             return HANDLED;
    614         }
    615     }
    616 
    617     class P2pEnablingState extends State {
    618         @Override
    619         public void enter() {
    620             if (DBG) logd(getName());
    621         }
    622 
    623         @Override
    624         public boolean processMessage(Message message) {
    625             if (DBG) logd(getName() + message.toString());
    626             switch (message.what) {
    627                 case WifiMonitor.SUP_CONNECTION_EVENT:
    628                     logd("P2p start successful");
    629                     transitionTo(mInactiveState);
    630                     break;
    631                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
    632                     if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
    633                         loge("Failed to start p2p, retry");
    634                         WifiNative.killSupplicant();
    635                         sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
    636                     } else {
    637                         loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
    638                         mP2pRestartCount = 0;
    639                     }
    640                     transitionTo(mP2pDisabledState);
    641                     break;
    642                 case WifiP2pManager.ENABLE_P2P:
    643                 case WifiP2pManager.DISABLE_P2P:
    644                     deferMessage(message);
    645                     break;
    646                 default:
    647                     return NOT_HANDLED;
    648             }
    649             return HANDLED;
    650         }
    651     }
    652 
    653     class P2pEnabledState extends State {
    654         @Override
    655         public void enter() {
    656             if (DBG) logd(getName());
    657             sendP2pStateChangedBroadcast(true);
    658             mNetworkInfo.setIsAvailable(true);
    659             initializeP2pSettings();
    660             showNotification();
    661         }
    662 
    663         @Override
    664         public boolean processMessage(Message message) {
    665             if (DBG) logd(getName() + message.toString());
    666             switch (message.what) {
    667                 case WifiP2pManager.ENABLE_P2P:
    668                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
    669                     break;
    670                 case WifiP2pManager.DISABLE_P2P:
    671                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
    672                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
    673                     transitionTo(mP2pDisablingState);
    674                     break;
    675                 case WifiP2pManager.DISCOVER_PEERS:
    676                     int timeout = message.arg1;
    677                     if (WifiNative.p2pFind(timeout)) {
    678                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
    679                     } else {
    680                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    681                                 WifiP2pManager.ERROR);
    682                     }
    683                     break;
    684                 case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
    685                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
    686                     if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
    687                     mPeers.update(device);
    688                     sendP2pPeersChangedBroadcast();
    689                     break;
    690                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
    691                     device = (WifiP2pDevice) message.obj;
    692                     if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
    693                     break;
    694                 case WifiP2pManager.CONNECT:
    695                     if (DBG) logd(getName() + " sending connect");
    696                     mSavedConnectConfig = (WifiP2pConfig) message.obj;
    697                     mPersistGroup = false;
    698                     int netId = configuredNetworkId(mSavedConnectConfig.deviceAddress);
    699                     if (netId >= 0) {
    700                         //TODO: if failure, remove config and do a regular p2pConnect()
    701                         WifiNative.p2pReinvoke(netId, mSavedConnectConfig.deviceAddress);
    702                     } else {
    703                         boolean join = false;
    704                         if (isGroupOwner(mSavedConnectConfig.deviceAddress)) join = true;
    705                         String pin = WifiNative.p2pConnect(mSavedConnectConfig, join);
    706                         try {
    707                             Integer.parseInt(pin);
    708                             notifyWpsPin(pin, mSavedConnectConfig.deviceAddress);
    709                         } catch (NumberFormatException ignore) {
    710                             // do nothing if p2pConnect did not return a pin
    711                         }
    712                     }
    713                     updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.INVITED);
    714                     sendP2pPeersChangedBroadcast();
    715                     replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
    716                     transitionTo(mGroupNegotiationState);
    717                     break;
    718                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
    719                     loge("Connection lost, restart p2p");
    720                     WifiNative.killSupplicant();
    721                     WifiNative.closeSupplicantConnection();
    722                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
    723                     transitionTo(mP2pDisabledState);
    724                     sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
    725                     break;
    726                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
    727                     mGroup = (WifiP2pGroup) message.obj;
    728                     if (DBG) logd(getName() + " group started");
    729                     if (mGroup.isGroupOwner()) {
    730                         startDhcpServer(mGroup.getInterface());
    731                     } else {
    732                         mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
    733                                 P2pStateMachine.this, mGroup.getInterface());
    734                         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
    735                         WifiP2pDevice groupOwner = mGroup.getOwner();
    736                         updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
    737                         sendP2pPeersChangedBroadcast();
    738                     }
    739                     transitionTo(mGroupCreatedState);
    740                     break;
    741                 default:
    742                     return NOT_HANDLED;
    743             }
    744             return HANDLED;
    745         }
    746 
    747         @Override
    748         public void exit() {
    749             sendP2pStateChangedBroadcast(false);
    750             mNetworkInfo.setIsAvailable(false);
    751             clearNotification();
    752         }
    753     }
    754 
    755     class InactiveState extends State {
    756         @Override
    757         public void enter() {
    758             if (DBG) logd(getName());
    759             //Start listening every time we get inactive
    760             WifiNative.p2pListen();
    761         }
    762 
    763         @Override
    764         public boolean processMessage(Message message) {
    765             if (DBG) logd(getName() + message.toString());
    766             switch (message.what) {
    767                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
    768                     mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj;
    769                     notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig);
    770                     transitionTo(mUserAuthorizingGroupNegotiationState);
    771                     break;
    772                 case WifiP2pManager.CREATE_GROUP:
    773                     mPersistGroup = true;
    774                     if (WifiNative.p2pGroupAdd()) {
    775                         replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
    776                     } else {
    777                         replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
    778                                 WifiP2pManager.ERROR);
    779                     }
    780                     transitionTo(mGroupNegotiationState);
    781                     break;
    782                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
    783                     WifiP2pGroup group = (WifiP2pGroup) message.obj;
    784                     notifyP2pInvitationReceived(group);
    785                     transitionTo(mUserAuthorizingGroupInvitationState);
    786                     break;
    787                 default:
    788                     return NOT_HANDLED;
    789             }
    790             return HANDLED;
    791         }
    792     }
    793 
    794     class UserAuthorizingGroupNegotiationState extends State {
    795         @Override
    796         public void enter() {
    797             if (DBG) logd(getName());
    798         }
    799 
    800         @Override
    801         public boolean processMessage(Message message) {
    802             if (DBG) logd(getName() + message.toString());
    803             switch (message.what) {
    804                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
    805                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
    806                     //Ignore additional connection requests
    807                     break;
    808                 case GROUP_NEGOTIATION_USER_ACCEPT:
    809                     sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
    810                     mSavedGoNegotiationConfig = null;
    811                     break;
    812                 case GROUP_NEGOTIATION_USER_REJECT:
    813                     if (DBG) logd("User rejected incoming negotiation request");
    814                     mSavedGoNegotiationConfig = null;
    815                     transitionTo(mInactiveState);
    816                     break;
    817                 default:
    818                     return NOT_HANDLED;
    819             }
    820             return HANDLED;
    821         }
    822     }
    823 
    824     class UserAuthorizingGroupInvitationState extends State {
    825         @Override
    826         public void enter() {
    827             if (DBG) logd(getName());
    828         }
    829 
    830         @Override
    831         public boolean processMessage(Message message) {
    832             if (DBG) logd(getName() + message.toString());
    833             switch (message.what) {
    834                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
    835                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
    836                     //Ignore additional connection requests
    837                     break;
    838                 case GROUP_INVITATION_USER_ACCEPT:
    839                     if (DBG) logd(getName() + " connect to invited group");
    840                     WifiP2pConfig config = new WifiP2pConfig();
    841                     config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
    842                     sendMessage(WifiP2pManager.CONNECT, config);
    843                     mSavedP2pGroup = null;
    844                     break;
    845                 case GROUP_INVITATION_USER_REJECT:
    846                     if (DBG) logd("User rejected incoming invitation request");
    847                     mSavedP2pGroup = null;
    848                     transitionTo(mInactiveState);
    849                     break;
    850                 default:
    851                     return NOT_HANDLED;
    852             }
    853             return HANDLED;
    854         }
    855     }
    856 
    857 
    858     class GroupNegotiationState extends State {
    859         @Override
    860         public void enter() {
    861             if (DBG) logd(getName());
    862             sendMessageDelayed(obtainMessage(GROUP_NEGOTIATION_TIMED_OUT,
    863                     ++mGroupNegotiationTimeoutIndex, 0), GROUP_NEGOTIATION_WAIT_TIME_MS);
    864         }
    865 
    866         @Override
    867         public boolean processMessage(Message message) {
    868             if (DBG) logd(getName() + message.toString());
    869             switch (message.what) {
    870                 // We ignore these right now, since we get a GROUP_STARTED notification
    871                 // afterwards
    872                 case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
    873                 case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
    874                     if (DBG) logd(getName() + " go success");
    875                     break;
    876                 case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
    877                 case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
    878                     if (DBG) logd(getName() + " go failure");
    879                     updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.FAILED);
    880                     mSavedConnectConfig = null;
    881                     sendP2pPeersChangedBroadcast();
    882                     transitionTo(mInactiveState);
    883                     break;
    884                 case GROUP_NEGOTIATION_TIMED_OUT:
    885                     if (mGroupNegotiationTimeoutIndex == message.arg1) {
    886                         if (DBG) logd("Group negotiation timed out");
    887                         updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.FAILED);
    888                         mSavedConnectConfig = null;
    889                         sendP2pPeersChangedBroadcast();
    890                         transitionTo(mInactiveState);
    891                     }
    892                     break;
    893                 case WifiP2pManager.DISCOVER_PEERS:
    894                     /* Discovery will break negotiation */
    895                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    896                             WifiP2pManager.BUSY);
    897                     break;
    898                 case WifiP2pManager.CANCEL_CONNECT:
    899                     if (WifiNative.p2pCancelConnect()) {
    900                         replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
    901                     } else {
    902                         replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
    903                                 WifiP2pManager.ERROR);
    904                     }
    905                     break;
    906                 default:
    907                     return NOT_HANDLED;
    908             }
    909             return HANDLED;
    910         }
    911     }
    912 
    913     class GroupCreatedState extends State {
    914         @Override
    915         public void enter() {
    916             if (DBG) logd(getName());
    917             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
    918 
    919             updateThisDevice(WifiP2pDevice.CONNECTED);
    920 
    921             //DHCP server has already been started if I am a group owner
    922             if (mGroup.isGroupOwner()) {
    923                 setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS);
    924                 sendP2pConnectionChangedBroadcast();
    925             }
    926         }
    927 
    928         @Override
    929         public boolean processMessage(Message message) {
    930             if (DBG) logd(getName() + message.toString());
    931             switch (message.what) {
    932                 case WifiMonitor.AP_STA_CONNECTED_EVENT:
    933                     //After a GO setup, STA connected event comes with interface address
    934                     String interfaceAddress = (String) message.obj;
    935                     String deviceAddress = getDeviceAddress(interfaceAddress);
    936                     if (deviceAddress != null) {
    937                         mGroup.addClient(deviceAddress);
    938                         updateDeviceStatus(deviceAddress, WifiP2pDevice.CONNECTED);
    939                         if (DBG) logd(getName() + " ap sta connected");
    940                         sendP2pPeersChangedBroadcast();
    941                     } else {
    942                         loge("Connect on unknown device address : " + interfaceAddress);
    943                     }
    944                     break;
    945                 case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
    946                     interfaceAddress = (String) message.obj;
    947                     deviceAddress = getDeviceAddress(interfaceAddress);
    948                     if (deviceAddress != null) {
    949                         updateDeviceStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
    950                         if (mGroup.removeClient(deviceAddress)) {
    951                             if (DBG) logd("Removed client " + deviceAddress);
    952                             if (!mPersistGroup && mGroup.isClientListEmpty()) {
    953                                 Slog.d(TAG, "Client list empty, remove non-persistent p2p group");
    954                                 WifiNative.p2pGroupRemove(mGroup.getInterface());
    955                             }
    956                         } else {
    957                             if (DBG) logd("Failed to remove client " + deviceAddress);
    958                             for (WifiP2pDevice c : mGroup.getClientList()) {
    959                                 if (DBG) logd("client " + c.deviceAddress);
    960                             }
    961                         }
    962                         sendP2pPeersChangedBroadcast();
    963                         if (DBG) loge(getName() + " ap sta disconnected");
    964                     } else {
    965                         loge("Disconnect on unknown device address : " + interfaceAddress);
    966                     }
    967                     break;
    968                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
    969                     DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj;
    970                     if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
    971                             dhcpInfo != null) {
    972                         if (DBG) logd("DhcpInfo: " + dhcpInfo);
    973                         setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress);
    974                         sendP2pConnectionChangedBroadcast();
    975                     } else {
    976                         WifiNative.p2pGroupRemove(mGroup.getInterface());
    977                     }
    978                     break;
    979                 case WifiP2pManager.REMOVE_GROUP:
    980                     if (DBG) loge(getName() + " remove group");
    981                     if (WifiNative.p2pGroupRemove(mGroup.getInterface())) {
    982                         replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
    983                     } else {
    984                         replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
    985                                 WifiP2pManager.ERROR);
    986                     }
    987                     break;
    988                 case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
    989                     if (DBG) loge(getName() + " group removed");
    990                     Collection <WifiP2pDevice> devices = mGroup.getClientList();
    991                     boolean changed = false;
    992                     for (WifiP2pDevice d : mPeers.getDeviceList()) {
    993                         if (devices.contains(d) || mGroup.getOwner().equals(d)) {
    994                             d.status = WifiP2pDevice.AVAILABLE;
    995                             changed = true;
    996                         }
    997                     }
    998 
    999                     if (mGroup.isGroupOwner()) {
   1000                         stopDhcpServer();
   1001                     } else {
   1002                         if (DBG) logd("stop DHCP client");
   1003                         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
   1004                         mDhcpStateMachine.quit();
   1005                         mDhcpStateMachine = null;
   1006                     }
   1007 
   1008                     mGroup = null;
   1009                     if (changed) sendP2pPeersChangedBroadcast();
   1010                     transitionTo(mInactiveState);
   1011                     break;
   1012                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
   1013                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
   1014                     if (device.equals(mGroup.getOwner())) {
   1015                         logd("Lost the group owner, killing p2p connection");
   1016                         WifiNative.p2pGroupRemove(mGroup.getInterface());
   1017                     } else if (mGroup.removeClient(device)) {
   1018                         if (!mPersistGroup && mGroup.isClientListEmpty()) {
   1019                             Slog.d(TAG, "Client list empty, removing a non-persistent p2p group");
   1020                             WifiNative.p2pGroupRemove(mGroup.getInterface());
   1021                         }
   1022                     }
   1023                     return NOT_HANDLED; // Do the regular device lost handling
   1024                 case WifiP2pManager.DISABLE_P2P:
   1025                     sendMessage(WifiP2pManager.REMOVE_GROUP);
   1026                     deferMessage(message);
   1027                     break;
   1028                 case WifiP2pManager.CONNECT:
   1029                     WifiP2pConfig config = (WifiP2pConfig) message.obj;
   1030                     logd("Inviting device : " + config.deviceAddress);
   1031                     if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) {
   1032                         updateDeviceStatus(config.deviceAddress, WifiP2pDevice.INVITED);
   1033                         sendP2pPeersChangedBroadcast();
   1034                         replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
   1035                     } else {
   1036                         replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
   1037                                 WifiP2pManager.ERROR);
   1038                     }
   1039                     // TODO: figure out updating the status to declined when invitation is rejected
   1040                     break;
   1041                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
   1042                     logd("===> INVITATION RESULT EVENT : " + message.obj);
   1043                     break;
   1044                 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
   1045                     notifyP2pProvDiscPbcRequest((WifiP2pDevice) message.obj);
   1046                     break;
   1047                 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
   1048                     notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj);
   1049                     break;
   1050                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
   1051                     Slog.e(TAG, "Duplicate group creation event notice, ignore");
   1052                     break;
   1053                 case WPS_PBC:
   1054                     WifiNative.wpsPbc();
   1055                     break;
   1056                 case WPS_PIN:
   1057                     WifiNative.wpsPin((String) message.obj);
   1058                     break;
   1059                 default:
   1060                     return NOT_HANDLED;
   1061             }
   1062             return HANDLED;
   1063         }
   1064 
   1065         public void exit() {
   1066             updateThisDevice(WifiP2pDevice.AVAILABLE);
   1067             setWifiP2pInfoOnGroupTermination();
   1068             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
   1069             sendP2pConnectionChangedBroadcast();
   1070         }
   1071     }
   1072 
   1073     private void sendP2pStateChangedBroadcast(boolean enabled) {
   1074         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
   1075         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1076         if (enabled) {
   1077             intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
   1078                     WifiP2pManager.WIFI_P2P_STATE_ENABLED);
   1079         } else {
   1080             intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
   1081                     WifiP2pManager.WIFI_P2P_STATE_DISABLED);
   1082         }
   1083         mContext.sendStickyBroadcast(intent);
   1084     }
   1085 
   1086     private void sendThisDeviceChangedBroadcast() {
   1087         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
   1088         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1089         intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
   1090         mContext.sendStickyBroadcast(intent);
   1091     }
   1092 
   1093     private void sendP2pPeersChangedBroadcast() {
   1094         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
   1095         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1096         mContext.sendBroadcast(intent);
   1097     }
   1098 
   1099     private void sendP2pConnectionChangedBroadcast() {
   1100         if (DBG) logd("sending p2p connection changed broadcast");
   1101         Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
   1102         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1103                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1104         intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
   1105         intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
   1106         mContext.sendStickyBroadcast(intent);
   1107     }
   1108 
   1109     private void startDhcpServer(String intf) {
   1110         InterfaceConfiguration ifcg = null;
   1111         try {
   1112             ifcg = mNwService.getInterfaceConfig(intf);
   1113             ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
   1114                         SERVER_ADDRESS), 24);
   1115             ifcg.interfaceFlags = "[up]";
   1116             mNwService.setInterfaceConfig(intf, ifcg);
   1117             /* This starts the dnsmasq server */
   1118             mNwService.startTethering(DHCP_RANGE);
   1119         } catch (Exception e) {
   1120             loge("Error configuring interface " + intf + ", :" + e);
   1121             return;
   1122         }
   1123 
   1124         logd("Started Dhcp server on " + intf);
   1125    }
   1126 
   1127     private void stopDhcpServer() {
   1128         try {
   1129             mNwService.stopTethering();
   1130         } catch (Exception e) {
   1131             loge("Error stopping Dhcp server" + e);
   1132             return;
   1133         }
   1134 
   1135         logd("Stopped Dhcp server");
   1136     }
   1137 
   1138     private void notifyP2pEnableFailure() {
   1139         Resources r = Resources.getSystem();
   1140         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1141             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1142             .setMessage(r.getString(R.string.wifi_p2p_failed_message))
   1143             .setPositiveButton(r.getString(R.string.ok), null)
   1144             .create();
   1145         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1146         dialog.show();
   1147     }
   1148 
   1149     private void notifyWpsPin(String pin, String peerAddress) {
   1150         Resources r = Resources.getSystem();
   1151         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1152             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1153             .setMessage(r.getString(R.string.wifi_p2p_pin_display_message, pin, peerAddress))
   1154             .setPositiveButton(r.getString(R.string.ok), null)
   1155             .create();
   1156         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1157         dialog.show();
   1158     }
   1159 
   1160     private void notifyP2pGoNegotationRequest(WifiP2pConfig config) {
   1161         Resources r = Resources.getSystem();
   1162         WpsInfo wps = config.wps;
   1163         final View textEntryView = LayoutInflater.from(mContext)
   1164                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1165         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1166 
   1167         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1168             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1169             .setView(textEntryView)
   1170             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1171                         public void onClick(DialogInterface dialog, int which) {
   1172                             if (DBG) logd(getName() + " connect " + pin.getText());
   1173 
   1174                             if (pin.getVisibility() == View.GONE) {
   1175                                 mSavedGoNegotiationConfig.wps.setup = WpsInfo.PBC;
   1176                             } else {
   1177                                 mSavedGoNegotiationConfig.wps.setup = WpsInfo.KEYPAD;
   1178                                 mSavedGoNegotiationConfig.wps.pin = pin.getText().toString();
   1179                             }
   1180                             sendMessage(GROUP_NEGOTIATION_USER_ACCEPT);
   1181                         }
   1182                     })
   1183             .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
   1184                         @Override
   1185                         public void onClick(DialogInterface dialog, int which) {
   1186                             if (DBG) logd(getName() + " ignore connect");
   1187                             sendMessage(GROUP_NEGOTIATION_USER_REJECT);
   1188                         }
   1189                     })
   1190             .create();
   1191 
   1192         if (wps.setup == WpsInfo.PBC) {
   1193             pin.setVisibility(View.GONE);
   1194             dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
   1195                         config.deviceAddress));
   1196         } else {
   1197             dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
   1198                         config.deviceAddress));
   1199         }
   1200 
   1201         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1202         dialog.show();
   1203     }
   1204 
   1205     private void notifyP2pProvDiscPbcRequest(WifiP2pDevice peer) {
   1206         Resources r = Resources.getSystem();
   1207         final View textEntryView = LayoutInflater.from(mContext)
   1208                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1209         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1210 
   1211         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1212             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1213             .setView(textEntryView)
   1214             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1215                         public void onClick(DialogInterface dialog, int which) {
   1216                                 if (DBG) logd(getName() + " wps_pbc");
   1217                                 sendMessage(WPS_PBC);
   1218                         }
   1219                     })
   1220             .setNegativeButton(r.getString(R.string.cancel), null)
   1221             .create();
   1222 
   1223         pin.setVisibility(View.GONE);
   1224         dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
   1225                         peer.deviceAddress));
   1226 
   1227         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1228         dialog.show();
   1229     }
   1230 
   1231     private void notifyP2pProvDiscPinRequest(WifiP2pDevice peer) {
   1232         Resources r = Resources.getSystem();
   1233         final View textEntryView = LayoutInflater.from(mContext)
   1234                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1235         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1236 
   1237         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1238             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1239             .setView(textEntryView)
   1240             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1241                     public void onClick(DialogInterface dialog, int which) {
   1242                         if (DBG) logd(getName() + " wps_pin");
   1243                         sendMessage(WPS_PIN, pin.getText().toString());
   1244                     }
   1245                     })
   1246             .setNegativeButton(r.getString(R.string.cancel), null)
   1247             .create();
   1248 
   1249         dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
   1250                         peer.deviceAddress));
   1251 
   1252         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1253         dialog.show();
   1254     }
   1255 
   1256     private void notifyP2pInvitationReceived(WifiP2pGroup group) {
   1257         mSavedP2pGroup = group;
   1258         Resources r = Resources.getSystem();
   1259         final View textEntryView = LayoutInflater.from(mContext)
   1260                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1261         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1262 
   1263         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1264             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1265             .setView(textEntryView)
   1266             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1267                         public void onClick(DialogInterface dialog, int which) {
   1268                             sendMessage(GROUP_INVITATION_USER_ACCEPT);
   1269                         }
   1270                     })
   1271             .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
   1272                         @Override
   1273                         public void onClick(DialogInterface dialog, int which) {
   1274                             if (DBG) logd(getName() + " ignore invite");
   1275                             sendMessage(GROUP_INVITATION_USER_REJECT);
   1276                         }
   1277                     })
   1278             .create();
   1279 
   1280         pin.setVisibility(View.GONE);
   1281         dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
   1282                         group.getOwner().deviceAddress));
   1283 
   1284         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1285         dialog.show();
   1286     }
   1287 
   1288     private void updateDeviceStatus(String deviceAddress, int status) {
   1289         for (WifiP2pDevice d : mPeers.getDeviceList()) {
   1290             if (d.deviceAddress.equals(deviceAddress)) {
   1291                 d.status = status;
   1292             }
   1293         }
   1294     }
   1295 
   1296     private boolean isGroupOwner(String deviceAddress) {
   1297         for (WifiP2pDevice d : mPeers.getDeviceList()) {
   1298             if (d.deviceAddress.equals(deviceAddress)) {
   1299                 return d.isGroupOwner();
   1300             }
   1301         }
   1302         return false;
   1303     }
   1304 
   1305     //TODO: implement when wpa_supplicant is fixed
   1306     private int configuredNetworkId(String deviceAddress) {
   1307         return -1;
   1308     }
   1309 
   1310     private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
   1311         mWifiP2pInfo.groupFormed = true;
   1312         mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
   1313         mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress);
   1314     }
   1315 
   1316     private void setWifiP2pInfoOnGroupTermination() {
   1317         mWifiP2pInfo.groupFormed = false;
   1318         mWifiP2pInfo.isGroupOwner = false;
   1319         mWifiP2pInfo.groupOwnerAddress = null;
   1320     }
   1321 
   1322     private String getDeviceAddress(String interfaceAddress) {
   1323         for (WifiP2pDevice d : mPeers.getDeviceList()) {
   1324             if (interfaceAddress.equals(WifiNative.p2pGetInterfaceAddress(d.deviceAddress))) {
   1325                 return d.deviceAddress;
   1326             }
   1327         }
   1328         return null;
   1329     }
   1330 
   1331     private void initializeP2pSettings() {
   1332         WifiNative.setPersistentReconnect(true);
   1333         WifiNative.setDeviceName(mThisDevice.deviceName);
   1334         WifiNative.setDeviceType(mThisDevice.primaryDeviceType);
   1335 
   1336         mThisDevice.deviceAddress = WifiNative.p2pGetDeviceAddress();
   1337         updateThisDevice(WifiP2pDevice.AVAILABLE);
   1338         if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress);
   1339     }
   1340 
   1341     private void updateThisDevice(int status) {
   1342         mThisDevice.status = status;
   1343         sendThisDeviceChangedBroadcast();
   1344     }
   1345 
   1346     //State machine initiated requests can have replyTo set to null indicating
   1347     //there are no recepients, we ignore those reply actions
   1348     private void replyToMessage(Message msg, int what) {
   1349         if (msg.replyTo == null) return;
   1350         Message dstMsg = obtainMessage(msg);
   1351         dstMsg.what = what;
   1352         mReplyChannel.replyToMessage(msg, dstMsg);
   1353     }
   1354 
   1355     private void replyToMessage(Message msg, int what, int arg1) {
   1356         if (msg.replyTo == null) return;
   1357         Message dstMsg = obtainMessage(msg);
   1358         dstMsg.what = what;
   1359         dstMsg.arg1 = arg1;
   1360         mReplyChannel.replyToMessage(msg, dstMsg);
   1361     }
   1362 
   1363     private void replyToMessage(Message msg, int what, Object obj) {
   1364         if (msg.replyTo == null) return;
   1365         Message dstMsg = obtainMessage(msg);
   1366         dstMsg.what = what;
   1367         dstMsg.obj = obj;
   1368         mReplyChannel.replyToMessage(msg, dstMsg);
   1369     }
   1370 
   1371     /* arg2 on the source message has a hash code that needs to be retained in replies
   1372      * see WifiP2pManager for details */
   1373     private Message obtainMessage(Message srcMsg) {
   1374         Message msg = Message.obtain();
   1375         msg.arg2 = srcMsg.arg2;
   1376         return msg;
   1377     }
   1378 
   1379     private void logd(String s) {
   1380         Slog.d(TAG, s);
   1381     }
   1382 
   1383     private void loge(String s) {
   1384         Slog.e(TAG, s);
   1385     }
   1386 
   1387     private void showNotification() {
   1388         NotificationManager notificationManager =
   1389             (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
   1390         if (notificationManager == null || mNotification != null) {
   1391             return;
   1392         }
   1393 
   1394         Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
   1395         intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
   1396 
   1397         PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
   1398 
   1399         Resources r = Resources.getSystem();
   1400         CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
   1401         CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
   1402 
   1403         mNotification = new Notification();
   1404         mNotification.when = 0;
   1405         //TODO: might change to be a seperate icon
   1406         mNotification.icon = R.drawable.stat_sys_tether_wifi;
   1407         mNotification.defaults &= ~Notification.DEFAULT_SOUND;
   1408         mNotification.flags = Notification.FLAG_ONGOING_EVENT;
   1409         mNotification.tickerText = title;
   1410         mNotification.setLatestEventInfo(mContext, title, message, pi);
   1411 
   1412         notificationManager.notify(mNotification.icon, mNotification);
   1413     }
   1414 
   1415     private void clearNotification() {
   1416         NotificationManager notificationManager =
   1417             (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
   1418         if (notificationManager != null && mNotification != null) {
   1419             notificationManager.cancel(mNotification.icon);
   1420             mNotification = null;
   1421         }
   1422     }
   1423 
   1424     private boolean isAirplaneSensitive() {
   1425         String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
   1426                 Settings.System.AIRPLANE_MODE_RADIOS);
   1427         return airplaneModeRadios == null
   1428             || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
   1429     }
   1430 
   1431     private boolean isAirplaneModeOn() {
   1432         return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
   1433                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
   1434     }
   1435 
   1436     }
   1437 }
   1438