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;
956bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff    /* Notification from DHCP state machine before quitting */
966bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff    public static final int CMD_ON_QUIT                     = BASE + 6;
97914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
98914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Command from controller to indicate DHCP discovery/renewal can continue
99914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * after pre DHCP action is complete */
1006bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff    public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = BASE + 7;
101914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
102914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /* Message.arg1 arguments to CMD_POST_DHCP notification */
103914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int DHCP_SUCCESS = 1;
104914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public static final int DHCP_FAILURE = 2;
105914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
106cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mDefaultState = new DefaultState();
107cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mStoppedState = new StoppedState();
108cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mWaitBeforeStartState = new WaitBeforeStartState();
109cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mRunningState = new RunningState();
110cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private State mWaitBeforeRenewalState = new WaitBeforeRenewalState();
111914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
112cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    private DhcpStateMachine(Context context, StateMachine controller, String intf) {
113914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        super(TAG);
114914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
115914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mContext = context;
116914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mController = controller;
117914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mInterfaceName = intf;
118914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
119914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
120914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        Intent dhcpRenewalIntent = new Intent(ACTION_DHCP_RENEW, null);
121914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mDhcpRenewalIntent = PendingIntent.getBroadcast(mContext, DHCP_RENEW, dhcpRenewalIntent, 0);
122914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
123914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
124914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mDhcpRenewWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
125cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        mDhcpRenewWakeLock.setReferenceCounted(false);
126914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
127914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mBroadcastReceiver = new BroadcastReceiver() {
128914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            @Override
129914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            public void onReceive(Context context, Intent intent) {
130914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                //DHCP renew
131914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                if (DBG) Log.d(TAG, "Sending a DHCP renewal " + this);
132cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                //Lock released after 40s in worst case scenario
133914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                mDhcpRenewWakeLock.acquire(40000);
134914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                sendMessage(CMD_RENEW_DHCP);
135914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
136914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        };
137914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_DHCP_RENEW));
138914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
139914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        addState(mDefaultState);
140914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mStoppedState, mDefaultState);
141914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mWaitBeforeStartState, mDefaultState);
142914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mRunningState, mDefaultState);
143914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            addState(mWaitBeforeRenewalState, mDefaultState);
144914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
145914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        setInitialState(mStoppedState);
146914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
147914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
148cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    public static DhcpStateMachine makeDhcpStateMachine(Context context, StateMachine controller,
149914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            String intf) {
150914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        DhcpStateMachine dsm = new DhcpStateMachine(context, controller, intf);
151914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        dsm.start();
152914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        return dsm;
153914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
154914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
155914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    /**
156914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * This sends a notification right before DHCP request/renewal so that the
157914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * controller can do certain actions before DHCP packets are sent out.
158914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * When the controller is ready, it sends a CMD_PRE_DHCP_ACTION_COMPLETE message
159914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * to indicate DHCP can continue
160914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     *
161914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * This is used by Wifi at this time for the purpose of doing BT-Wifi coex
162914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     * handling during Dhcp
163914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff     */
164914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    public void registerForPreDhcpNotification() {
165914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        mRegisteredForPreDhcpNotification = true;
166914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
167914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
168bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville    /**
169bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville     * Quit the DhcpStateMachine.
170bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville     *
171bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville     * @hide
172bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville     */
173bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville    public void doQuit() {
174bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville        quit();
175bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville    }
176bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville
1776bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff    protected void onQuitting() {
1786bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff        mController.sendMessage(CMD_ON_QUIT);
1796bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff    }
1806bfc88876ab575913299b477528225a4d7bf8232Irfan Sheriff
181cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class DefaultState extends State {
182914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
183bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville        public void exit() {
184bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville            mContext.unregisterReceiver(mBroadcastReceiver);
185bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville        }
186bbf30dfd767f823f5f40d14b498e2a593454c5c9Wink Saville        @Override
187914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
188914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
189914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
190914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_RENEW_DHCP:
191914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    Log.e(TAG, "Error! Failed to handle a DHCP renewal on " + mInterfaceName);
192cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                    mDhcpRenewWakeLock.release();
193914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
194914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
195914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    Log.e(TAG, "Error! unhandled message  " + message);
196914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
197914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
198914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return HANDLED;
199914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
200914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
201914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
202914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
203cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class StoppedState extends State {
204914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
205914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
206914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
207914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
208914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
209914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
210914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
211914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
212914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
213914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
214914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
215914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (mRegisteredForPreDhcpNotification) {
216914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        /* Notify controller before starting DHCP */
217914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
218914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mWaitBeforeStartState);
219914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
220914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        if (runDhcp(DhcpAction.START)) {
221914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                            transitionTo(mRunningState);
222914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        }
223914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
224914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
225914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
226914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
227914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
228914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
229914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
230914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
231914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
232914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
233914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
234914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
235914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
236cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class WaitBeforeStartState extends State {
237914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
238914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
239914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
240914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
241914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
242914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
243914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
244914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
245914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
246914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
247914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_PRE_DHCP_ACTION_COMPLETE:
248914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (runDhcp(DhcpAction.START)) {
249914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mRunningState);
250914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
251914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mStoppedState);
252914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
253914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
254914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
255914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    transitionTo(mStoppedState);
256914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
257914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
258914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
259914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
260914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
261914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
262914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
263914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
264914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
265914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
266914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
267914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
268cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class RunningState extends State {
269914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
270914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
271914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
272914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
273914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
274914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
275914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
276914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
277914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
278914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
279914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
280914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    mAlarmManager.cancel(mDhcpRenewalIntent);
281914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
282914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
283914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
284914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    transitionTo(mStoppedState);
285914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
286914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_RENEW_DHCP:
287914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (mRegisteredForPreDhcpNotification) {
288914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        /* Notify controller before starting DHCP */
289914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        mController.sendMessage(CMD_PRE_DHCP_ACTION);
290914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        transitionTo(mWaitBeforeRenewalState);
291cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                        //mDhcpRenewWakeLock is released in WaitBeforeRenewalState
292914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
293914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        if (!runDhcp(DhcpAction.RENEW)) {
294914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                            transitionTo(mStoppedState);
295914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        }
296cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff                        mDhcpRenewWakeLock.release();
297914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
298914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
299914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
300914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
301914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
302914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
303914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
304914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
305914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
306914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
307914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
308914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
309cf99765b6255d73aa4fbc61d5bd78ec65fbd0370Irfan Sheriff    class WaitBeforeRenewalState extends State {
310914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
311914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public void enter() {
312914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + "\n");
313914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
314914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
315914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        @Override
316914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        public boolean processMessage(Message message) {
317914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            boolean retValue = HANDLED;
318914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
319914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            switch (message.what) {
320914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_STOP_DHCP:
321914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    mAlarmManager.cancel(mDhcpRenewalIntent);
322914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
323914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
324914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
325914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    transitionTo(mStoppedState);
326914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
327914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_PRE_DHCP_ACTION_COMPLETE:
328914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    if (runDhcp(DhcpAction.RENEW)) {
329914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                       transitionTo(mRunningState);
330914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    } else {
331914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                       transitionTo(mStoppedState);
332914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    }
333914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
334914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                case CMD_START_DHCP:
335914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    //ignore
336914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
337914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                default:
338914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    retValue = NOT_HANDLED;
339914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    break;
340914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            }
341914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            return retValue;
342914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
343cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        @Override
344cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        public void exit() {
345cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff            mDhcpRenewWakeLock.release();
346cd672ebf2963e4c448115bac4c80a6659a5904beIrfan Sheriff        }
347914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
348914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
349914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    private boolean runDhcp(DhcpAction dhcpAction) {
350914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        boolean success = false;
351914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
352914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
353914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        if (dhcpAction == DhcpAction.START) {
3547f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff            if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
355914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            success = NetworkUtils.runDhcp(mInterfaceName, dhcpInfoInternal);
3562c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff            mDhcpInfo = dhcpInfoInternal;
357914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        } else if (dhcpAction == DhcpAction.RENEW) {
3587f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff            if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
359914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpInfoInternal);
3602c08ede34ceb0f847cc9f996db9832f5358f8726Irfan Sheriff            dhcpInfoInternal.updateFromDhcpRequest(mDhcpInfo);
361914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
362914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
363914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        if (success) {
3647f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff            if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
365b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff            long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
366b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff
367b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff            //Sanity check for renewal
368b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff            if (leaseDuration >= 0) {
369b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                //TODO: would be good to notify the user that his network configuration is
370b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
371b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
372b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                    leaseDuration = MIN_RENEWAL_TIME_SECS;
373b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                }
374b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                //Do it a bit earlier than half the lease duration time
375b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                //to beat the native DHCP client and avoid extra packets
376b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                //48% for one hour lease time = 29 minutes
377b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
378b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                        SystemClock.elapsedRealtime() +
379b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                        leaseDuration * 480, //in milliseconds
380b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                        mDhcpRenewalIntent);
381b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff            } else {
382b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff                //infinite lease time, no renewal needed
383b9c955664bf300deb11e5aaf88e8ff4d11b26a73Irfan Sheriff            }
384914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff
385914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
386914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                .sendToTarget();
387914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        } else {
3887f8a12c75cf2b376fce58fc22b5ecb1b64acf110Irfan Sheriff            Log.e(TAG, "DHCP failed on " + mInterfaceName + ": " +
389914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                    NetworkUtils.getDhcpError());
390914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            NetworkUtils.stopDhcp(mInterfaceName);
391914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
392914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff                .sendToTarget();
393914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        }
394914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff        return success;
395914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff    }
396914ed90f2c02092474d2db36626734ca1b2cf315Irfan Sheriff}
397