OSDN Git Service

Merge "Fix ISO country code of Slovenia." into honeycomb-mr2
[android-x86/frameworks-base.git] / core / java / android / net / DhcpStateMachine.java
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;
18
19 import com.android.internal.util.Protocol;
20 import com.android.internal.util.HierarchicalState;
21 import com.android.internal.util.HierarchicalStateMachine;
22
23 import android.app.AlarmManager;
24 import android.app.PendingIntent;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.net.DhcpInfoInternal;
30 import android.net.NetworkUtils;
31 import android.os.Message;
32 import android.os.PowerManager;
33 import android.os.SystemClock;
34 import android.util.Log;
35
36 /**
37  * StateMachine that interacts with the native DHCP client and can talk to
38  * a controller that also needs to be a StateMachine
39  *
40  * The Dhcp state machine provides the following features:
41  * - Wakeup and renewal using the native DHCP client  (which will not renew
42  *   on its own when the device is in suspend state and this can lead to device
43  *   holding IP address beyond expiry)
44  * - A notification right before DHCP request or renewal is started. This
45  *   can be used for any additional setup before DHCP. For example, wifi sets
46  *   BT-Wifi coex settings right before DHCP is initiated
47  *
48  * @hide
49  */
50 public class DhcpStateMachine extends HierarchicalStateMachine {
51
52     private static final String TAG = "DhcpStateMachine";
53     private static final boolean DBG = false;
54
55
56     /* A StateMachine that controls the DhcpStateMachine */
57     private HierarchicalStateMachine mController;
58
59     private Context mContext;
60     private BroadcastReceiver mBroadcastReceiver;
61     private AlarmManager mAlarmManager;
62     private PendingIntent mDhcpRenewalIntent;
63     private PowerManager.WakeLock mDhcpRenewWakeLock;
64     private static final String WAKELOCK_TAG = "DHCP";
65
66     private static final int DHCP_RENEW = 0;
67     private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
68
69     private enum DhcpAction {
70         START,
71         RENEW
72     };
73
74     private String mInterfaceName;
75     private boolean mRegisteredForPreDhcpNotification = false;
76
77     private static final int BASE = Protocol.BASE_DHCP;
78
79     /* Commands from controller to start/stop DHCP */
80     public static final int CMD_START_DHCP                  = BASE + 1;
81     public static final int CMD_STOP_DHCP                   = BASE + 2;
82     public static final int CMD_RENEW_DHCP                  = BASE + 3;
83
84     /* Notification from DHCP state machine prior to DHCP discovery/renewal */
85     public static final int CMD_PRE_DHCP_ACTION             = BASE + 4;
86     /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
87      * success/failure */
88     public static final int CMD_POST_DHCP_ACTION            = BASE + 5;
89
90     /* Command from controller to indicate DHCP discovery/renewal can continue
91      * after pre DHCP action is complete */
92     public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = BASE + 6;
93
94     /* Message.arg1 arguments to CMD_POST_DHCP notification */
95     public static final int DHCP_SUCCESS = 1;
96     public static final int DHCP_FAILURE = 2;
97
98     private HierarchicalState mDefaultState = new DefaultState();
99     private HierarchicalState mStoppedState = new StoppedState();
100     private HierarchicalState mWaitBeforeStartState = new WaitBeforeStartState();
101     private HierarchicalState mRunningState = new RunningState();
102     private HierarchicalState mWaitBeforeRenewalState = new WaitBeforeRenewalState();
103
104     private DhcpStateMachine(Context context, HierarchicalStateMachine controller, String intf) {
105         super(TAG);
106
107         mContext = context;
108         mController = controller;
109         mInterfaceName = intf;
110
111         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
112         Intent dhcpRenewalIntent = new Intent(ACTION_DHCP_RENEW, null);
113         mDhcpRenewalIntent = PendingIntent.getBroadcast(mContext, DHCP_RENEW, dhcpRenewalIntent, 0);
114
115         PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
116         mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
117
118         mBroadcastReceiver = new BroadcastReceiver() {
119             @Override
120             public void onReceive(Context context, Intent intent) {
121                 //DHCP renew
122                 if (DBG) Log.d(TAG, "Sending a DHCP renewal " + this);
123                 //acquire a 40s wakelock to finish DHCP renewal
124                 mDhcpRenewWakeLock.acquire(40000);
125                 sendMessage(CMD_RENEW_DHCP);
126             }
127         };
128         mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_DHCP_RENEW));
129
130         addState(mDefaultState);
131             addState(mStoppedState, mDefaultState);
132             addState(mWaitBeforeStartState, mDefaultState);
133             addState(mRunningState, mDefaultState);
134             addState(mWaitBeforeRenewalState, mDefaultState);
135
136         setInitialState(mStoppedState);
137     }
138
139     public static DhcpStateMachine makeDhcpStateMachine(Context context, HierarchicalStateMachine controller,
140             String intf) {
141         DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
142         dsm.start();
143         return dsm;
144     }
145
146     /**
147      * This sends a notification right before DHCP request/renewal so that the
148      * controller can do certain actions before DHCP packets are sent out.
149      * When the controller is ready, it sends a CMD_PRE_DHCP_ACTION_COMPLETE message
150      * to indicate DHCP can continue
151      *
152      * This is used by Wifi at this time for the purpose of doing BT-Wifi coex
153      * handling during Dhcp
154      */
155     public void registerForPreDhcpNotification() {
156         mRegisteredForPreDhcpNotification = true;
157     }
158
159     class DefaultState extends HierarchicalState {
160         @Override
161         public boolean processMessage(Message message) {
162             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
163             switch (message.what) {
164                 case CMD_RENEW_DHCP:
165                     Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
166                     break;
167                 case HSM_QUIT_CMD:
168                     mContext.unregisterReceiver(mBroadcastReceiver);
169                     //let parent kill the state machine
170                     return NOT_HANDLED;
171                 default:
172                     Log.e(TAG, "Error! unhandled message  " + message);
173                     break;
174             }
175             return HANDLED;
176         }
177     }
178
179
180     class StoppedState extends HierarchicalState {
181         @Override
182         public void enter() {
183             if (DBG) Log.d(TAG, getName() + "\n");
184         }
185
186         @Override
187         public boolean processMessage(Message message) {
188             boolean retValue = HANDLED;
189             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
190             switch (message.what) {
191                 case CMD_START_DHCP:
192                     if (mRegisteredForPreDhcpNotification) {
193                         /* Notify controller before starting DHCP */
194                         mController.sendMessage(CMD_PRE_DHCP_ACTION);
195                         transitionTo(mWaitBeforeStartState);
196                     } else {
197                         if (runDhcp(DhcpAction.START)) {
198                             transitionTo(mRunningState);
199                         }
200                     }
201                     break;
202                 case CMD_STOP_DHCP:
203                     //ignore
204                     break;
205                 default:
206                     retValue = NOT_HANDLED;
207                     break;
208             }
209             return retValue;
210         }
211     }
212
213     class WaitBeforeStartState extends HierarchicalState {
214         @Override
215         public void enter() {
216             if (DBG) Log.d(TAG, getName() + "\n");
217         }
218
219         @Override
220         public boolean processMessage(Message message) {
221             boolean retValue = HANDLED;
222             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
223             switch (message.what) {
224                 case CMD_PRE_DHCP_ACTION_COMPLETE:
225                     if (runDhcp(DhcpAction.START)) {
226                         transitionTo(mRunningState);
227                     } else {
228                         transitionTo(mStoppedState);
229                     }
230                     break;
231                 case CMD_STOP_DHCP:
232                     transitionTo(mStoppedState);
233                     break;
234                 case CMD_START_DHCP:
235                     //ignore
236                     break;
237                 default:
238                     retValue = NOT_HANDLED;
239                     break;
240             }
241             return retValue;
242         }
243     }
244
245     class RunningState extends HierarchicalState {
246         @Override
247         public void enter() {
248             if (DBG) Log.d(TAG, getName() + "\n");
249         }
250
251         @Override
252         public boolean processMessage(Message message) {
253             boolean retValue = HANDLED;
254             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
255             switch (message.what) {
256                 case CMD_STOP_DHCP:
257                     mAlarmManager.cancel(mDhcpRenewalIntent);
258                     if (!NetworkUtils.stopDhcp(mInterfaceName)) {
259                         Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
260                     }
261                     transitionTo(mStoppedState);
262                     break;
263                 case CMD_RENEW_DHCP:
264                     if (mRegisteredForPreDhcpNotification) {
265                         /* Notify controller before starting DHCP */
266                         mController.sendMessage(CMD_PRE_DHCP_ACTION);
267                         transitionTo(mWaitBeforeRenewalState);
268                     } else {
269                         if (!runDhcp(DhcpAction.RENEW)) {
270                             transitionTo(mStoppedState);
271                         }
272                     }
273                     break;
274                 case CMD_START_DHCP:
275                     //ignore
276                     break;
277                 default:
278                     retValue = NOT_HANDLED;
279             }
280             return retValue;
281         }
282     }
283
284     class WaitBeforeRenewalState extends HierarchicalState {
285         @Override
286         public void enter() {
287             if (DBG) Log.d(TAG, getName() + "\n");
288         }
289
290         @Override
291         public boolean processMessage(Message message) {
292             boolean retValue = HANDLED;
293             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
294             switch (message.what) {
295                 case CMD_STOP_DHCP:
296                     mAlarmManager.cancel(mDhcpRenewalIntent);
297                     if (!NetworkUtils.stopDhcp(mInterfaceName)) {
298                         Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
299                     }
300                     transitionTo(mStoppedState);
301                     break;
302                 case CMD_PRE_DHCP_ACTION_COMPLETE:
303                     if (runDhcp(DhcpAction.RENEW)) {
304                        transitionTo(mRunningState);
305                     } else {
306                        transitionTo(mStoppedState);
307                     }
308                     break;
309                 case CMD_START_DHCP:
310                     //ignore
311                     break;
312                 default:
313                     retValue = NOT_HANDLED;
314                     break;
315             }
316             return retValue;
317         }
318     }
319
320     private boolean runDhcp(DhcpAction dhcpAction) {
321         boolean success = false;
322         DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
323
324         if (dhcpAction == DhcpAction.START) {
325             Log.d(TAG, "DHCP request on " + mInterfaceName);
326             success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
327         } else if (dhcpAction == DhcpAction.RENEW) {
328             Log.d(TAG, "DHCP renewal on " + mInterfaceName);
329             success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
330         }
331
332         if (success) {
333             Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
334             //Do it a bit earlier than half the lease duration time
335             //to beat the native DHCP client and avoid extra packets
336             //48% for one hour lease time = 29 minutes
337             mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
338                     SystemClock.elapsedRealtime() +
339                     dhcpInfoInternal.leaseDuration * 480, //in milliseconds
340                     mDhcpRenewalIntent);
341
342             mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
343                 .sendToTarget();
344         } else {
345             Log.d(TAG, "DHCP failed on " + mInterfaceName + ": " +
346                     NetworkUtils.getDhcpError());
347             NetworkUtils.stopDhcp(mInterfaceName);
348             mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
349                 .sendToTarget();
350         }
351         return success;
352     }
353 }