DhcpStateMachine.java revision 2c08ede34ceb0f847cc9f996db9832f5358f8726
1914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff/*
2914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * Copyright (C) 2011 The Android Open Source Project
3914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *
4914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * Licensed under the Apache License, Version 2.0 (the "License");
5914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * you may not use this file except in compliance with the License.
6914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * You may obtain a copy of the License at
7914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *
8914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *      http://www.apache.org/licenses/LICENSE-2.0
9914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *
10914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * Unless required by applicable law or agreed to in writing, software
11914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * distributed under the License is distributed on an "AS IS" BASIS,
12914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * See the License for the specific language governing permissions and
14914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * limitations under the License.
15914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff */
16914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
17914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffpackage android.net;
18914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
19914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport com.android.internal.util.Protocol;
20cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriffimport com.android.internal.util.State;
21cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriffimport com.android.internal.util.StateMachine;
22914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
23914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.app.AlarmManager;
24914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.app.PendingIntent;
25914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.content.BroadcastReceiver;
26914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.content.Context;
27914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.content.Intent;
28914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.content.IntentFilter;
29914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.net.DhcpInfoInternal;
30914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.net.NetworkUtils;
31914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.os.Message;
32914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.os.PowerManager;
33914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.os.SystemClock;
34914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriffimport android.util.Log;
35914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
36914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff/**
37914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * StateMachine that interacts with the native DHCP client and can talk to
38914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * a controller that also needs to be a StateMachine
39914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *
40914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * The Dhcp state machine provides the following features:
41914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * - Wakeup and renewal using the native DHCP client  (which will not renew
42914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *   on its own when the device is in suspend state and this can lead to device
43914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *   holding IP address beyond expiry)
44914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * - A notification right before DHCP request or renewal is started. This
45914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *   can be used for any additional setup before DHCP. For example, wifi sets
46914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *   BT-Wifi coex settings right before DHCP is initiated
47914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff *
48914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff * @hide
49914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff */
50cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriffpublic class DhcpStateMachine extends StateMachine {
51914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
52914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private static final String TAG = "DhcpStateMachine";
53914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private static final boolean DBG = false;
54914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
55914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
56914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* A StateMachine that controls the DhcpStateMachine */
57cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private StateMachine mController;
58914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
59914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private Context mContext;
60914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private BroadcastReceiver mBroadcastReceiver;
61914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private AlarmManager mAlarmManager;
62914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private PendingIntent mDhcpRenewalIntent;
63914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private PowerManager.WakeLock mDhcpRenewWakeLock;
64914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private static final String WAKELOCK_TAG = "DHCP";
65914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
662c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff    //Remember DHCP configuration from first request
672c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff    private DhcpInfoInternal mDhcpInfo;
682c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff
69914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private static final int DHCP_RENEW = 0;
70914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private static final String ACTION_DHCP_RENEW = "android.net.wifi.DHCP_RENEW";
71914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
7241b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff    //Used for sanity check on setting up renewal
7341b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff    private static final int MIN_RENEWAL_TIME_SECS = 5 * 60;  // 5 minutes
7441b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff
75914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private enum DhcpAction {
76914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        START,
77914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        RENEW
78914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    };
79914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
80914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private String mInterfaceName;
81914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private boolean mRegisteredForPreDhcpNotification = false;
82914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
83914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private static final int BASE = Protocol.BASE_DHCP;
84914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
85914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Commands from controller to start/stop DHCP */
86914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int CMD_START_DHCP                  = BASE + 1;
87914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int CMD_STOP_DHCP                   = BASE + 2;
88914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int CMD_RENEW_DHCP                  = BASE + 3;
89914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
90914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Notification from DHCP state machine prior to DHCP discovery/renewal */
91914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int CMD_PRE_DHCP_ACTION             = BASE + 4;
92914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
93914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * success/failure */
94914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int CMD_POST_DHCP_ACTION            = BASE + 5;
95914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
96914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Command from controller to indicate DHCP discovery/renewal can continue
97914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * after pre DHCP action is complete */
98914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = BASE + 6;
99914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
100914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Message.arg1 arguments to CMD_POST_DHCP notification */
101914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int DHCP_SUCCESS = 1;
102914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int DHCP_FAILURE = 2;
103914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
104cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mDefaultState = new DefaultState();
105cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mStoppedState = new StoppedState();
106cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mWaitBeforeStartState = new WaitBeforeStartState();
107cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mRunningState = new RunningState();
108cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mWaitBeforeRenewalState = new WaitBeforeRenewalState();
109914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
110cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private DhcpStateMachine(Context context, StateMachine controller, String intf) {
111914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        super(TAG);
112914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
113914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mContext = context;
114914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mController = controller;
115914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mInterfaceName = intf;
116914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
117914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
118914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        Intent dhcpRenewalIntent = new Intent(ACTION_DHCP_RENEW, null);
119914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mDhcpRenewalIntent = PendingIntent.getBroadcast(mContext, DHCP_RENEW, dhcpRenewalIntent, 0);
120914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
121914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
122914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
123cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        mDhcpRenewWakeLock.setReferenceCounted(false);
124914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
125914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mBroadcastReceiver = new BroadcastReceiver() {
126914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            @Override
127914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            public void onReceive(Context context, Intent intent) {
128914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                //DHCP renew
129914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                if (DBG) Log.d(TAG, "Sending a DHCP renewal " + this);
130cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                //Lock released after 40s in worst case scenario
131914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                mDhcpRenewWakeLock.acquire(40000);
132914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                sendMessage(CMD_RENEW_DHCP);
133914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
134914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        };
135914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_DHCP_RENEW));
136914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
137914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        addState(mDefaultState);
138914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mStoppedState, mDefaultState);
139914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mWaitBeforeStartState, mDefaultState);
140914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mRunningState, mDefaultState);
141914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mWaitBeforeRenewalState, mDefaultState);
142914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
143914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        setInitialState(mStoppedState);
144914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
145914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
146cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    public static DhcpStateMachine makeDhcpStateMachine(Context context, StateMachine controller,
147914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            String intf) {
148914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
149914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        dsm.start();
150914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        return dsm;
151914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
152914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
153914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /**
154914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * This sends a notification right before DHCP request/renewal so that the
155914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * controller can do certain actions before DHCP packets are sent out.
156914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * When the controller is ready, it sends a CMD_PRE_DHCP_ACTION_COMPLETE message
157914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * to indicate DHCP can continue
158914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     *
159914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * This is used by Wifi at this time for the purpose of doing BT-Wifi coex
160914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * handling during Dhcp
161914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     */
162914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public void registerForPreDhcpNotification() {
163914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mRegisteredForPreDhcpNotification = true;
164914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
165914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
166cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class DefaultState extends State {
167914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
168914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
169914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
170914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
171914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_RENEW_DHCP:
172914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
173cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                    mDhcpRenewWakeLock.release();
174914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
175cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff                case SM_QUIT_CMD:
176914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    mContext.unregisterReceiver(mBroadcastReceiver);
177914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //let parent kill the state machine
178914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    return NOT_HANDLED;
179914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
180914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    Log.e(TAG, "Error! unhandled message  " + message);
181914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
182914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
183914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return HANDLED;
184914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
185914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
186914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
187914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
188cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class StoppedState extends State {
189914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
190914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
191914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
192914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
193914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
194914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
195914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
196914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
197914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
198914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
199914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
200914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (mRegisteredForPreDhcpNotification) {
201914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        /* Notify controller before starting DHCP */
202914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
203914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mWaitBeforeStartState);
204914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
205914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        if (runDhcp(DhcpAction.START)) {
206914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                            transitionTo(mRunningState);
207914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        }
208914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
209914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
210914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
211914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
212914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
213914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
214914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
215914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
216914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
217914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
218914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
219914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
220914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
221cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class WaitBeforeStartState extends State {
222914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
223914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
224914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
225914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
226914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
227914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
228914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
229914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
230914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
231914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
232914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_PRE_DHCP_ACTION_COMPLETE:
233914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (runDhcp(DhcpAction.START)) {
234914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mRunningState);
235914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
236914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mStoppedState);
237914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
238914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
239914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
240914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    transitionTo(mStoppedState);
241914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
242914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
243914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
244914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
245914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
246914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
247914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
248914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
249914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
250914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
251914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
252914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
253cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class RunningState extends State {
254914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
255914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
256914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
257914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
258914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
259914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
260914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
261914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
262914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
263914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
264914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
265914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    mAlarmManager.cancel(mDhcpRenewalIntent);
266914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
267914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
268914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
269914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    transitionTo(mStoppedState);
270914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
271914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_RENEW_DHCP:
272914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (mRegisteredForPreDhcpNotification) {
273914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        /* Notify controller before starting DHCP */
274914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
275914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mWaitBeforeRenewalState);
276cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                        //mDhcpRenewWakeLock is released in WaitBeforeRenewalState
277914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
278914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        if (!runDhcp(DhcpAction.RENEW)) {
279914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                            transitionTo(mStoppedState);
280914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        }
281cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                        mDhcpRenewWakeLock.release();
282914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
283914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
284914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
285914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
286914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
287914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
288914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
289914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
290914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
291914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
292914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
293914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
294cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class WaitBeforeRenewalState extends State {
295914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
296914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
297914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
298914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
299914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
300914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
301914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
302914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
303914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
304914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
305914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
306914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    mAlarmManager.cancel(mDhcpRenewalIntent);
307914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
308914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
309914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
310914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    transitionTo(mStoppedState);
311914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
312914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_PRE_DHCP_ACTION_COMPLETE:
313914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (runDhcp(DhcpAction.RENEW)) {
314914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                       transitionTo(mRunningState);
315914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
316914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                       transitionTo(mStoppedState);
317914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
318914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
319914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
320914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
321914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
322914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
323914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
324914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
325914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
326914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
327914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
328cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        @Override
329cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        public void exit() {
330cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff            mDhcpRenewWakeLock.release();
331cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        }
332914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
333914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
334914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private boolean runDhcp(DhcpAction dhcpAction) {
335914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        boolean success = false;
336914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
337914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
338914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        if (dhcpAction == DhcpAction.START) {
339914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            Log.d(TAG, "DHCP request on " + mInterfaceName);
340914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
3412c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff            mDhcpInfo = dhcpInfoInternal;
342914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        } else if (dhcpAction == DhcpAction.RENEW) {
343914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            Log.d(TAG, "DHCP renewal on " + mInterfaceName);
344914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
3452c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff            dhcpInfoInternal.updateFromDhcpRequest(mDhcpInfo);
346914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
347914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
348914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        if (success) {
349914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
35041b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
35141b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff
35241b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           //Sanity check for renewal
35341b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           //TODO: would be good to notify the user that his network configuration is
35441b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
35541b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
35641b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff               leaseDuration = MIN_RENEWAL_TIME_SECS;
35741b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           }
35841b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           //Do it a bit earlier than half the lease duration time
35941b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           //to beat the native DHCP client and avoid extra packets
36041b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           //48% for one hour lease time = 29 minutes
36141b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff           mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
36241b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff                   SystemClock.elapsedRealtime() +
36341b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff                   leaseDuration * 480, //in milliseconds
36441b35884ff68a62f5fc8f65dcc26342d0889113dIrfan Sheriff                   mDhcpRenewalIntent);
365914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
366914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
367914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                .sendToTarget();
368914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        } else {
369914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            Log.d(TAG, "DHCP failed on " + mInterfaceName + ": " +
370914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    NetworkUtils.getDhcpError());
371914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            NetworkUtils.stopDhcp(mInterfaceName);
372914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
373914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                .sendToTarget();
374914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
375914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        return success;
376914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
377914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff}
378