diff options
23 files changed, 4391 insertions, 3312 deletions
diff --git a/api/current.xml b/api/current.xml index a8287369c42d..a7e6c526d3da 100644 --- a/api/current.xml +++ b/api/current.xml @@ -22143,6 +22143,17 @@ visibility="protected" > </field> +<field name="POP_BACK_STACK_INCLUSIVE" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="RESULT_CANCELED" type="int" transient="false" @@ -41168,6 +41179,17 @@ visibility="public" > </field> +<field name="DOWNLOAD_SERVICE" + type="java.lang.String" + transient="false" + volatile="false" + value=""download"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="DROPBOX_SERVICE" type="java.lang.String" transient="false" @@ -96740,6 +96762,567 @@ > </field> </class> +<class name="DownloadManager" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="enqueue" + return="long" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="request" type="android.net.DownloadManager.Request"> +</parameter> +</method> +<method name="openDownloadedFile" + return="android.os.ParcelFileDescriptor" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="long"> +</parameter> +<exception name="FileNotFoundException" type="java.io.FileNotFoundException"> +</exception> +</method> +<method name="query" + return="android.database.Cursor" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="query" type="android.net.DownloadManager.Query"> +</parameter> +</method> +<method name="remove" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="long"> +</parameter> +</method> +<field name="ACTION_DOWNLOAD_COMPLETE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.DOWNLOAD_COMPLETE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ACTION_NOTIFICATION_CLICKED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_BYTES_DOWNLOADED_SO_FAR" + type="java.lang.String" + transient="false" + volatile="false" + value=""bytes_so_far"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_DESCRIPTION" + type="java.lang.String" + transient="false" + volatile="false" + value=""description"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_ERROR_CODE" + type="java.lang.String" + transient="false" + volatile="false" + value=""error_code"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_LAST_MODIFIED_TIMESTAMP" + type="java.lang.String" + transient="false" + volatile="false" + value=""last_modified_timestamp"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_LOCAL_URI" + type="java.lang.String" + transient="false" + volatile="false" + value=""local_uri"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_MEDIA_TYPE" + type="java.lang.String" + transient="false" + volatile="false" + value=""media_type"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_STATUS" + type="java.lang.String" + transient="false" + volatile="false" + value=""status"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_TITLE" + type="java.lang.String" + transient="false" + volatile="false" + value=""title"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_TOTAL_SIZE_BYTES" + type="java.lang.String" + transient="false" + volatile="false" + value=""total_size"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="COLUMN_URI" + type="java.lang.String" + transient="false" + volatile="false" + value=""uri"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_DEVICE_NOT_FOUND" + type="int" + transient="false" + volatile="false" + value="1007" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_FILE_ERROR" + type="int" + transient="false" + volatile="false" + value="1001" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_HTTP_DATA_ERROR" + type="int" + transient="false" + volatile="false" + value="1004" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_INSUFFICIENT_SPACE" + type="int" + transient="false" + volatile="false" + value="1006" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_TOO_MANY_REDIRECTS" + type="int" + transient="false" + volatile="false" + value="1005" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_UNHANDLED_HTTP_CODE" + type="int" + transient="false" + volatile="false" + value="1002" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="ERROR_UNKNOWN" + type="int" + transient="false" + volatile="false" + value="1000" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DOWNLOAD_ID" + type="java.lang.String" + transient="false" + volatile="false" + value=""extra_download_id"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_FAILED" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_PAUSED" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_PENDING" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_RUNNING" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="STATUS_SUCCESSFUL" + type="int" + transient="false" + volatile="false" + value="8" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<class name="DownloadManager.Query" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="DownloadManager.Query" + type="android.net.DownloadManager.Query" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="setFilterById" + return="android.net.DownloadManager.Query" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="long"> +</parameter> +</method> +<method name="setFilterByStatus" + return="android.net.DownloadManager.Query" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="flags" type="int"> +</parameter> +</method> +</class> +<class name="DownloadManager.Request" + extends="java.lang.Object" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="DownloadManager.Request" + type="android.net.DownloadManager.Request" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="android.net.Uri"> +</parameter> +</constructor> +<method name="setAllowedNetworkTypes" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="flags" type="int"> +</parameter> +</method> +<method name="setAllowedOverRoaming" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="allowed" type="boolean"> +</parameter> +</method> +<method name="setDescription" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="description" type="java.lang.String"> +</parameter> +</method> +<method name="setDestinationUri" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="android.net.Uri"> +</parameter> +</method> +<method name="setMediaType" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="mediaType" type="java.lang.String"> +</parameter> +</method> +<method name="setRequestHeader" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="header" type="java.lang.String"> +</parameter> +<parameter name="value" type="java.lang.String"> +</parameter> +</method> +<method name="setShowNotification" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="flags" type="int"> +</parameter> +</method> +<method name="setTitle" + return="android.net.DownloadManager.Request" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="title" type="java.lang.String"> +</parameter> +</method> +<field name="NETWORK_MOBILE" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="NETWORK_WIFI" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="NETWORK_WIMAX" + type="int" + transient="false" + volatile="false" + value="4" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="NOTIFICATION_WHEN_RUNNING" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <class name="LocalServerSocket" extends="java.lang.Object" abstract="false" @@ -154172,6 +154755,36 @@ > </method> </class> +<class name="LoaderTestCase" + extends="android.test.AndroidTestCase" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="LoaderTestCase" + type="android.test.LoaderTestCase" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<method name="getLoaderResultSynchronously" + return="T" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="loader" type="android.content.Loader<T>"> +</parameter> +</method> +</class> <class name="MoreAsserts" extends="java.lang.Object" abstract="false" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 61fd5f3690a3..142c32580c5f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2059,7 +2059,7 @@ public class Activity extends ContextThemeWrapper * removed. Otherwise, all entries up to but not including that entry * will be removed */ - static final int POP_BACK_STACK_INCLUSIVE = 1<<0; + public static final int POP_BACK_STACK_INCLUSIVE = 1<<0; /** * Pop the top state off the back stack. Returns true if there was one diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 697a987d5f20..b7a750b6573e 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -400,7 +400,15 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java * start_compatibility} - * + * + * <p class="caution">Note that the system calls this on your + * service's main thread. A service's main thread is the same + * thread where UI operations place for Activities running in the + * same process. You should always avoid stalling the main + * thread's event loop. When doing long-running operations, + * network calls, or heavy disk I/O, you should kick off a new + * thread, or use {@link android.os.AsyncTask}.</p> + * * @param intent The Intent supplied to {@link android.content.Context#startService}, * as given. This may be null if the service is being restarted after * its process has gone away, and it had previously returned anything diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 5fb2aaec83d7..40108c36f306 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -57,6 +57,7 @@ import java.util.ArrayList; * * <p>The primary methods that need to be implemented are: * <ul> + * <li>{@link #onCreate} which is called to initialize the provider</li> * <li>{@link #query} which returns data to the caller</li> * <li>{@link #insert} which inserts new data into the content provider</li> * <li>{@link #update} which updates existing data in the content provider</li> @@ -64,8 +65,15 @@ import java.util.ArrayList; * <li>{@link #getType} which returns the MIME type of data in the content provider</li> * </ul></p> * - * <p>This class takes care of cross process calls so subclasses don't have to worry about which - * process a request is coming from.</p> + * <p class="caution">Data access methods (such as {@link #insert} and + * {@link #update}) may be called from many threads at once, and must be thread-safe. + * Other methods (such as {@link #onCreate}) are only called from the application + * main thread, and must avoid performing lengthy operations. See the method + * descriptions for their expected thread behavior.</p> + * + * <p>Requests to {@link ContentResolver} are automatically forwarded to the appropriate + * ContentProvider instance, so subclasses don't have to worry about the details of + * cross-process calls.</p> */ public abstract class ContentProvider implements ComponentCallbacks { /* @@ -81,6 +89,21 @@ public abstract class ContentProvider implements ComponentCallbacks { private Transport mTransport = new Transport(); + /** + * Construct a ContentProvider instance. Content providers must be + * <a href="{@docRoot}guide/topics/manifest/provider-element.html">declared + * in the manifest</a>, accessed with {@link ContentResolver}, and created + * automatically by the system, so applications usually do not create + * ContentProvider instances directly. + * + * <p>At construction time, the object is uninitialized, and most fields and + * methods are unavailable. Subclasses should initialize themselves in + * {@link #onCreate}, not the constructor. + * + * <p>Content providers are created on the application main thread at + * application launch time. The constructor must not perform lengthy + * operations, or application startup will be delayed. + */ public ContentProvider() { } @@ -328,8 +351,8 @@ public abstract class ContentProvider implements ComponentCallbacks { /** - * Retrieve the Context this provider is running in. Only available once - * onCreate(Map icicle) has been called -- this will be null in the + * Retrieves the Context this provider is running in. Only available once + * {@link #onCreate} has been called -- this will return null in the * constructor. */ public final Context getContext() { @@ -403,23 +426,59 @@ public abstract class ContentProvider implements ComponentCallbacks { } /** - * Called when the provider is being started. + * Implement this to initialize your content provider on startup. + * This method is called for all registered content providers on the + * application main thread at application launch time. It must not perform + * lengthy operations, or application startup will be delayed. + * + * <p>You should defer nontrivial initialization (such as opening, + * upgrading, and scanning databases) until the content provider is used + * (via {@link #query}, {@link #insert}, etc). Deferred initialization + * keeps application startup fast, avoids unnecessary work if the provider + * turns out not to be needed, and stops database errors (such as a full + * disk) from halting application launch. + * + * <p>For SQL databases, {@link android.database.sqlite.SQLiteOpenHelper} + * is a helpful utility class that makes it easy to manage databases, + * and will automatically defer opening until first use. If you do use + * SQLiteOpenHelper, make sure to avoid calling + * {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} or + * {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} + * from this method. (Instead, override + * {@link android.database.sqlite.SQLiteOpenHelper#onOpen} to initialize the + * database when it is first opened.) * * @return true if the provider was successfully loaded, false otherwise */ public abstract boolean onCreate(); + /** + * {@inheritDoc} + * This method is always called on the application main thread, and must + * not perform lengthy operations. + * + * <p>The default content provider implementation does nothing. + * Override this method to take appropriate action. + * (Content providers do not usually care about things like screen + * orientation, but may want to know about locale changes.) + */ public void onConfigurationChanged(Configuration newConfig) { } - + + /** + * {@inheritDoc} + * This method is always called on the application main thread, and must + * not perform lengthy operations. + * + * <p>The default content provider implementation does nothing. + * Subclasses may override this method to take appropriate action. + */ public void onLowMemory() { } /** - * Receives a query request from a client in a local process, and - * returns a Cursor. This is called internally by the {@link ContentResolver}. - * This method can be called from multiple - * threads, as described in + * Implement this to handle query requests from clients. + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * <p> @@ -476,11 +535,11 @@ public abstract class ContentProvider implements ComponentCallbacks { String selection, String[] selectionArgs, String sortOrder); /** - * Return the MIME type of the data at the given URI. This should start with + * Implement this to handle requests for the MIME type of the data at the + * given URI. The returned MIME type should start with * <code>vnd.android.cursor.item</code> for a single record, * or <code>vnd.android.cursor.dir/</code> for multiple items. - * This method can be called from multiple - * threads, as described in + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * @@ -490,11 +549,10 @@ public abstract class ContentProvider implements ComponentCallbacks { public abstract String getType(Uri uri); /** - * Implement this to insert a new row. + * Implement this to handle requests to insert a new row. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. - * This method can be called from multiple - * threads, as described in + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * @param uri The content:// URI of the insertion request. @@ -504,12 +562,12 @@ public abstract class ContentProvider implements ComponentCallbacks { public abstract Uri insert(Uri uri, ContentValues values); /** - * Implement this to insert a set of new rows, or the default implementation will - * iterate over the values and call {@link #insert} on each of them. + * Override this to handle requests to insert a set of new rows, or the + * default implementation will iterate over the values and call + * {@link #insert} on each of them. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after inserting. - * This method can be called from multiple - * threads, as described in + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * @@ -526,13 +584,12 @@ public abstract class ContentProvider implements ComponentCallbacks { } /** - * A request to delete one or more rows. The selection clause is applied when performing - * the deletion, allowing the operation to affect multiple rows in a - * directory. + * Implement this to handle requests to delete one or more rows. + * The implementation should apply the selection clause when performing + * deletion, allowing the operation to affect multiple rows in a directory. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()} * after deleting. - * This method can be called from multiple - * threads, as described in + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * @@ -549,13 +606,12 @@ public abstract class ContentProvider implements ComponentCallbacks { public abstract int delete(Uri uri, String selection, String[] selectionArgs); /** - * Update a content URI. All rows matching the optionally provided selection - * will have their columns listed as the keys in the values map with the - * values of those keys. + * Implement this to update one or more rows. + * The implementation should update all rows matching the selection + * to set the columns according to the provided values map. * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()} * after updating. - * This method can be called from multiple - * threads, as described in + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. * @@ -570,18 +626,15 @@ public abstract class ContentProvider implements ComponentCallbacks { String[] selectionArgs); /** - * Open a file blob associated with a content URI. - * This method can be called from multiple - * threads, as described in + * Override this to open a file blob associated with a content URI. + * The default implementation always throws {@link FileNotFoundException}. + * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: * Processes and Threads</a>. - * - * <p>Returns a - * ParcelFileDescriptor, from which you can obtain a - * {@link java.io.FileDescriptor} for use with - * {@link java.io.FileInputStream}, {@link java.io.FileOutputStream}, etc. - * This can be used to store large data (such as an image) associated with - * a particular piece of content. + * + * <p>Returns a ParcelFileDescriptor, which is returned directly to the + * caller. This way large data (such as images and documents) can be + * returned without copying the content. * * <p>The returned ParcelFileDescriptor is owned by the caller, so it is * their responsibility to close it when done. That is, the implementation @@ -599,31 +652,35 @@ public abstract class ContentProvider implements ComponentCallbacks { * no file associated with the given URI or the mode is invalid. * @throws SecurityException Throws SecurityException if the caller does * not have permission to access the file. - * + * * @see #openAssetFile(Uri, String) * @see #openFileHelper(Uri, String) - */ + */ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { throw new FileNotFoundException("No files supported by provider at " + uri); } - + /** * This is like {@link #openFile}, but can be implemented by providers * that need to be able to return sub-sections of files, often assets - * inside of their .apk. Note that when implementing this your clients - * must be able to deal with such files, either directly with - * {@link ContentResolver#openAssetFileDescriptor - * ContentResolver.openAssetFileDescriptor}, or by using the higher-level + * inside of their .apk. + * This method can be called from multiple threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. + * + * <p>If you implement this, your clients must be able to deal with such + * files, either directly with + * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level * {@link ContentResolver#openInputStream ContentResolver.openInputStream} * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream} * methods. - * - * <p><em>Note: if you are implementing this to return a full file, you + * + * <p class="note">If you are implementing this to return a full file, you * should create the AssetFileDescriptor with * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with - * applications that can not handle sub-sections of files.</em></p> + * applications that can not handle sub-sections of files.</p> * * @param uri The URI whose file is to be opened. * @param mode Access mode for the file. May be "r" for read-only access, @@ -735,17 +792,20 @@ public abstract class ContentProvider implements ComponentCallbacks { } /** - * Applies each of the {@link ContentProviderOperation} objects and returns an array - * of their results. Passes through OperationApplicationException, which may be thrown - * by the call to {@link ContentProviderOperation#apply}. - * If all the applications succeed then a {@link ContentProviderResult} array with the - * same number of elements as the operations will be returned. It is implementation-specific - * how many, if any, operations will have been successfully applied if a call to - * apply results in a {@link OperationApplicationException}. + * Override this to perform a batch of operations, or the default + * implementation will {@link ContentProviderOperation#apply} each of the + * {@link ContentProviderOperation} objects. If the apply calls all succeed + * then a {@link ContentProviderResult} array with the same number of + * elements as the operations will be returned. If any of the apply calls + * fail, it is up to the implementation how many of the others take effect. + * This method can be called from multiple threads, as described in + * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals: + * Processes and Threads</a>. + * * @param operations the operations to apply * @return the results of the applications - * @throws OperationApplicationException thrown if an application fails. - * See {@link ContentProviderOperation#apply} for more information. + * @throws OperationApplicationException thrown if any operation fails. + * @see ContentProviderOperation#apply */ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3d64984a8ffc..b5ec633ce276 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1566,7 +1566,6 @@ public abstract class Context { * {@link android.net.DownloadManager} for requesting HTTP downloads. * * @see #getSystemService - * @hide (TODO) for now */ public static final String DOWNLOAD_SERVICE = "download"; diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 0f2e872e54e1..d8cce216db75 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -24,10 +24,16 @@ import android.util.Log; /** * A helper class to manage database creation and version management. - * You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and + * + * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and * optionally {@link #onOpen}, and this class takes care of opening the database * if it exists, creating it if it does not, and upgrading it as necessary. * Transactions are used to make sure the database is always in a sensible state. + * + * <p>This class makes it easy for {@link android.content.ContentProvider} + * implementations to defer opening and upgrading the database until first use, + * to avoid blocking application startup with long-running database upgrades. + * * <p>For an example, see the NotePadProvider class in the NotePad sample application, * in the <em>samples/</em> directory of the SDK.</p> * @@ -51,8 +57,9 @@ public abstract class SQLiteOpenHelper { /** * Create a helper object to create, open, and/or manage a database. - * The database is not actually created or opened until one of - * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called. + * This method always returns very quickly. The database is not actually + * created or opened until one of {@link #getWritableDatabase} or + * {@link #getReadableDatabase} is called. * * @param context to use to open or create the database * @param name of the database file, or null for an in-memory database @@ -96,13 +103,20 @@ public abstract class SQLiteOpenHelper { /** * Create and/or open a database that will be used for reading and writing. - * Once opened successfully, the database is cached, so you can call this - * method every time you need to write to the database. Make sure to call - * {@link #close} when you no longer need it. + * The first time this is called, the database will be opened and + * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be + * called. * - * <p>Errors such as bad permissions or a full disk may cause this operation + * <p>Once opened successfully, the database is cached, so you can + * call this method every time you need to write to the database. + * (Make sure to call {@link #close} when you no longer need the database.) + * Errors such as bad permissions or a full disk may cause this method * to fail, but future attempts may succeed if the problem is fixed.</p> * + * <p class="caution">Database upgrade may take a long time, you + * should not call this method from the application main thread, including + * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. + * * @throws SQLiteException if the database cannot be opened for writing * @return a read/write database object valid until {@link #close} is called */ @@ -183,6 +197,11 @@ public abstract class SQLiteOpenHelper { * database object will be closed and the read/write object will be returned * in the future. * + * <p class="caution">Like {@link #getWritableDatabase}, this method may + * take a long time to return, so you should not call it from the + * application main thread, including from + * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. + * * @throws SQLiteException if the database cannot be opened * @return a database object valid until {@link #getWritableDatabase} * or {@link #close} is called. @@ -262,9 +281,9 @@ public abstract class SQLiteOpenHelper { public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); /** - * Called when the database has been opened. - * Override method should check {@link SQLiteDatabase#isReadOnly} before - * updating the database. + * Called when the database has been opened. The implementation + * should check {@link SQLiteDatabase#isReadOnly} before updating the + * database. * * @param db The database. */ diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java index 455bd36e5a3e..1d88c18ea511 100644 --- a/core/java/android/net/DownloadManager.java +++ b/core/java/android/net/DownloadManager.java @@ -42,8 +42,6 @@ import java.util.Set; * Instances of this class should be obtained through * {@link android.content.Context#getSystemService(String)} by passing * {@link android.content.Context#DOWNLOAD_SERVICE}. - * - * @hide */ public class DownloadManager { /** diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 21f711cfcabf..5f5e11c96a2b 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -97,7 +97,7 @@ public class NetworkInfo implements Parcelable { stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED); stateMap.put(DetailedState.FAILED, State.DISCONNECTED); } - + private int mNetworkType; private int mSubtype; private String mTypeName; @@ -144,7 +144,9 @@ public class NetworkInfo implements Parcelable { * @return the network type */ public int getType() { - return mNetworkType; + synchronized (this) { + return mNetworkType; + } } /** @@ -153,12 +155,16 @@ public class NetworkInfo implements Parcelable { * @return the network subtype */ public int getSubtype() { - return mSubtype; + synchronized (this) { + return mSubtype; + } } void setSubtype(int subtype, String subtypeName) { - mSubtype = subtype; - mSubtypeName = subtypeName; + synchronized (this) { + mSubtype = subtype; + mSubtypeName = subtypeName; + } } /** @@ -167,7 +173,9 @@ public class NetworkInfo implements Parcelable { * @return the name of the network type */ public String getTypeName() { - return mTypeName; + synchronized (this) { + return mTypeName; + } } /** @@ -175,7 +183,9 @@ public class NetworkInfo implements Parcelable { * @return the name of the network subtype */ public String getSubtypeName() { - return mSubtypeName; + synchronized (this) { + return mSubtypeName; + } } /** @@ -188,7 +198,9 @@ public class NetworkInfo implements Parcelable { * of being established, {@code false} otherwise. */ public boolean isConnectedOrConnecting() { - return mState == State.CONNECTED || mState == State.CONNECTING; + synchronized (this) { + return mState == State.CONNECTED || mState == State.CONNECTING; + } } /** @@ -197,7 +209,9 @@ public class NetworkInfo implements Parcelable { * @return {@code true} if network connectivity exists, {@code false} otherwise. */ public boolean isConnected() { - return mState == State.CONNECTED; + synchronized (this) { + return mState == State.CONNECTED; + } } /** @@ -213,7 +227,9 @@ public class NetworkInfo implements Parcelable { * @return {@code true} if the network is available, {@code false} otherwise */ public boolean isAvailable() { - return mIsAvailable; + synchronized (this) { + return mIsAvailable; + } } /** @@ -223,7 +239,9 @@ public class NetworkInfo implements Parcelable { * @hide */ public void setIsAvailable(boolean isAvailable) { - mIsAvailable = isAvailable; + synchronized (this) { + mIsAvailable = isAvailable; + } } /** @@ -234,7 +252,9 @@ public class NetworkInfo implements Parcelable { * otherwise. */ public boolean isFailover() { - return mIsFailover; + synchronized (this) { + return mIsFailover; + } } /** @@ -244,7 +264,9 @@ public class NetworkInfo implements Parcelable { * @hide */ public void setFailover(boolean isFailover) { - mIsFailover = isFailover; + synchronized (this) { + mIsFailover = isFailover; + } } /** @@ -254,11 +276,15 @@ public class NetworkInfo implements Parcelable { * @return {@code true} if roaming is in effect, {@code false} otherwise. */ public boolean isRoaming() { - return mIsRoaming; + synchronized (this) { + return mIsRoaming; + } } void setRoaming(boolean isRoaming) { - mIsRoaming = isRoaming; + synchronized (this) { + mIsRoaming = isRoaming; + } } /** @@ -266,7 +292,9 @@ public class NetworkInfo implements Parcelable { * @return the coarse-grained state */ public State getState() { - return mState; + synchronized (this) { + return mState; + } } /** @@ -274,7 +302,9 @@ public class NetworkInfo implements Parcelable { * @return the fine-grained state */ public DetailedState getDetailedState() { - return mDetailedState; + synchronized (this) { + return mDetailedState; + } } /** @@ -287,10 +317,12 @@ public class NetworkInfo implements Parcelable { * @hide */ public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) { - this.mDetailedState = detailedState; - this.mState = stateMap.get(detailedState); - this.mReason = reason; - this.mExtraInfo = extraInfo; + synchronized (this) { + this.mDetailedState = detailedState; + this.mState = stateMap.get(detailedState); + this.mReason = reason; + this.mExtraInfo = extraInfo; + } } /** @@ -299,7 +331,9 @@ public class NetworkInfo implements Parcelable { * @return the reason for failure, or null if not available */ public String getReason() { - return mReason; + synchronized (this) { + return mReason; + } } /** @@ -309,20 +343,24 @@ public class NetworkInfo implements Parcelable { * @return the extra information, or null if not available */ public String getExtraInfo() { - return mExtraInfo; + synchronized (this) { + return mExtraInfo; + } } @Override public String toString() { - StringBuilder builder = new StringBuilder("NetworkInfo: "); - builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()). - append("], state: ").append(mState).append("/").append(mDetailedState). - append(", reason: ").append(mReason == null ? "(unspecified)" : mReason). - append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo). - append(", roaming: ").append(mIsRoaming). - append(", failover: ").append(mIsFailover). - append(", isAvailable: ").append(mIsAvailable); - return builder.toString(); + synchronized (this) { + StringBuilder builder = new StringBuilder("NetworkInfo: "); + builder.append("type: ").append(getTypeName()).append("[").append(getSubtypeName()). + append("], state: ").append(mState).append("/").append(mDetailedState). + append(", reason: ").append(mReason == null ? "(unspecified)" : mReason). + append(", extra: ").append(mExtraInfo == null ? "(none)" : mExtraInfo). + append(", roaming: ").append(mIsRoaming). + append(", failover: ").append(mIsFailover). + append(", isAvailable: ").append(mIsAvailable); + return builder.toString(); + } } /** @@ -338,17 +376,19 @@ public class NetworkInfo implements Parcelable { * @hide */ public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mNetworkType); - dest.writeInt(mSubtype); - dest.writeString(mTypeName); - dest.writeString(mSubtypeName); - dest.writeString(mState.name()); - dest.writeString(mDetailedState.name()); - dest.writeInt(mIsFailover ? 1 : 0); - dest.writeInt(mIsAvailable ? 1 : 0); - dest.writeInt(mIsRoaming ? 1 : 0); - dest.writeString(mReason); - dest.writeString(mExtraInfo); + synchronized (this) { + dest.writeInt(mNetworkType); + dest.writeInt(mSubtype); + dest.writeString(mTypeName); + dest.writeString(mSubtypeName); + dest.writeString(mState.name()); + dest.writeString(mDetailedState.name()); + dest.writeInt(mIsFailover ? 1 : 0); + dest.writeInt(mIsAvailable ? 1 : 0); + dest.writeInt(mIsRoaming ? 1 : 0); + dest.writeString(mReason); + dest.writeString(mExtraInfo); + } } /** diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index dc9259078152..d4b0500a5ac1 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -97,7 +97,7 @@ public final class StrictMode { * via Parcel.writeNoException() (amusingly) where the caller can * choose how to react. */ - private static ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations = + private static final ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>> gatheredViolations = new ThreadLocal<ArrayList<ApplicationErrorReport.CrashInfo>>() { @Override protected ArrayList<ApplicationErrorReport.CrashInfo> initialValue() { // Starts null to avoid unnecessary allocations when diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 739244248281..aec537d246b6 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -98,6 +98,11 @@ static jstring doStringCommand(JNIEnv *env, const char *cmd) } } +static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject clazz) +{ + return (jboolean)(::is_wifi_driver_loaded() == 1); +} + static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz) { return (jboolean)(::wifi_load_driver() == 0); @@ -524,6 +529,7 @@ static JNINativeMethod gWifiMethods[] = { /* name, signature, funcPtr */ { "loadDriver", "()Z", (void *)android_net_wifi_loadDriver }, + { "isDriverLoaded", "()Z", (void *)android_net_wifi_isDriverLoaded}, { "unloadDriver", "()Z", (void *)android_net_wifi_unloadDriver }, { "startSupplicant", "()Z", (void *)android_net_wifi_startSupplicant }, { "stopSupplicant", "()Z", (void *)android_net_wifi_stopSupplicant }, diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 040b32494def..5c4e4fd971dc 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -1566,15 +1566,15 @@ static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstri if (parcel != NULL) { const jchar* str = env->GetStringCritical(name, 0); if (str) { - const int32_t old_strict_policy = - IPCThreadState::self()->getStrictModePolicy(); - int32_t strict_policy; - bool isValid = parcel->enforceInterface( + IPCThreadState* threadState = IPCThreadState::self(); + const int32_t oldPolicy = threadState->getStrictModePolicy(); + const bool isValid = parcel->enforceInterface( String16(str, env->GetStringLength(name)), - &strict_policy); + threadState); env->ReleaseStringCritical(name, str); if (isValid) { - if (old_strict_policy != strict_policy) { + const int32_t newPolicy = threadState->getStrictModePolicy(); + if (oldPolicy != newPolicy) { // Need to keep the Java-level thread-local strict // mode policy in sync for the libcore // enforcements, which involves an upcall back @@ -1582,7 +1582,7 @@ static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstri // Parcel.enforceInterface signature, as it's // pseudo-public, and used via AIDL // auto-generation...) - set_dalvik_blockguard_policy(env, strict_policy); + set_dalvik_blockguard_policy(env, newPolicy); } return; // everything was correct -> return silently } diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index fd0fc1f3067c..32c9a1d5a9b6 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -26,11 +26,12 @@ // --------------------------------------------------------------------------- namespace android { +class Flattenable; class IBinder; +class IPCThreadState; class ProcessState; class String8; class TextOutput; -class Flattenable; struct flat_binder_object; // defined in support_p/binder_module.h @@ -61,10 +62,13 @@ public: // Parses the RPC header, returning true if the interface name // in the header matches the expected interface from the caller. - // If strict_policy_out is non-NULL, the RPC header's StrictMode policy - // mask is returned. + // + // Additionally, enforceInterface does part of the work of + // propagating the StrictMode policy mask, populating the current + // IPCThreadState, which as an optimization may optionally be + // passed in. bool enforceInterface(const String16& interface, - int32_t* strict_policy_out = NULL) const; + IPCThreadState* threadState = NULL) const; bool checkInterface(IBinder*) const; void freeData(); diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 60babad379fd..18f75df2ea2f 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -458,13 +458,13 @@ bool Parcel::checkInterface(IBinder* binder) const } bool Parcel::enforceInterface(const String16& interface, - int32_t* strict_policy_out) const + IPCThreadState* threadState) const { - int32_t strict_policy = readInt32(); - IPCThreadState::self()->setStrictModePolicy(strict_policy); - if (strict_policy_out != NULL) { - *strict_policy_out = strict_policy; + int32_t strictPolicy = readInt32(); + if (threadState == NULL) { + threadState = IPCThreadState::self(); } + threadState->setStrictModePolicy(strictPolicy); const String16 str(readString16()); if (str == interface) { return true; diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 9c504feac92b..311efc8286e8 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -88,6 +88,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private List mNetRequestersPids[]; + private WifiWatchdogService mWifiWatchdogService; + // priority order of the nettrackers // (excluding dynamically set mNetworkPreference) // TODO - move mNetworkTypePreference into this @@ -318,6 +320,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; wst.startMonitoring(); + //TODO: as part of WWS refactor, create only when needed + mWifiWatchdogService = new WifiWatchdogService(context, wst); + break; case ConnectivityManager.TYPE_MOBILE: mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler, diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java index 0de11c65c856..14b7d3ec3891 100644 --- a/services/java/com/android/server/DropBoxManagerService.java +++ b/services/java/com/android/server/DropBoxManagerService.java @@ -690,8 +690,6 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub { // was lost. Tombstones are expunged by age (see above). if (mAllFiles.blocks > mCachedQuotaBlocks) { - Slog.i(TAG, "Usage (" + mAllFiles.blocks + ") > Quota (" + mCachedQuotaBlocks + ")"); - // Find a fair share amount of space to limit each tag int unsqueezed = mAllFiles.blocks, squeezed = 0; TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values()); diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 35baaa702e0a..15080b2f25b3 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,6 @@ package com.android.server; -import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; -import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; -import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; -import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; -import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; - -import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; -import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; - import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothA2dp; @@ -41,7 +29,6 @@ import android.content.pm.PackageManager; import android.net.wifi.IWifiManager; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; -import android.net.wifi.WifiNative; import android.net.wifi.WifiStateTracker; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; @@ -51,56 +38,43 @@ import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; import android.net.NetworkStateTracker; import android.net.DhcpInfo; -import android.net.NetworkUtils; import android.os.Binder; -import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.INetworkManagementService; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.util.Slog; -import android.text.TextUtils; import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.regex.Pattern; +import java.util.concurrent.atomic.AtomicBoolean; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.net.UnknownHostException; import com.android.internal.app.IBatteryStats; -import android.app.backup.IBackupManager; import com.android.server.am.BatteryStatsService; import com.android.internal.R; /** * WifiService handles remote WiFi operation requests by implementing - * the IWifiManager interface. It also creates a WifiMonitor to listen - * for Wifi-related events. + * the IWifiManager interface. * * @hide */ +//TODO: Clean up multiple locks and implement WifiService +// as a SM to track soft AP/client/adhoc bring up based +// on device idle state, airplane mode and boot. + public class WifiService extends IWifiManager.Stub { private static final String TAG = "WifiService"; - private static final boolean DBG = false; - private static final Pattern scanResultPattern = Pattern.compile("\t+"); + private static final boolean DBG = true; + private final WifiStateTracker mWifiStateTracker; - /* TODO: fetch a configurable interface */ - private static final String SOFTAP_IFACE = "wl0.1"; private Context mContext; - private int mWifiApState; private AlarmManager mAlarmManager; private PendingIntent mIdleIntent; @@ -109,10 +83,8 @@ public class WifiService extends IWifiManager.Stub { private boolean mDeviceIdle; private int mPluggedType; - private enum DriverAction {DRIVER_UNLOAD, NO_DRIVER_UNLOAD}; - // true if the user enabled Wifi while in airplane mode - private boolean mAirplaneModeOverwridden; + private AtomicBoolean mAirplaneModeOverwridden = new AtomicBoolean(false); private final LockList mLocks = new LockList(); // some wifi lock statistics @@ -128,9 +100,7 @@ public class WifiService extends IWifiManager.Stub { private final IBatteryStats mBatteryStats; - private INetworkManagementService nwService; ConnectivityManager mCm; - private WifiWatchdogService mWifiWatchdogService = null; private String[] mWifiRegexs; /** @@ -142,46 +112,6 @@ public class WifiService extends IWifiManager.Stub { */ private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */ - private static final String WAKELOCK_TAG = "WifiService"; - - // Wake lock used by other operations - private static PowerManager.WakeLock sWakeLock; - - private static final int MESSAGE_ENABLE_WIFI = 0; - private static final int MESSAGE_DISABLE_WIFI = 1; - private static final int MESSAGE_STOP_WIFI = 2; - private static final int MESSAGE_START_WIFI = 3; - private static final int MESSAGE_UPDATE_STATE = 5; - private static final int MESSAGE_START_ACCESS_POINT = 6; - private static final int MESSAGE_STOP_ACCESS_POINT = 7; - private static final int MESSAGE_SET_CHANNELS = 8; - - - private final WifiHandler mWifiHandler; - - /* - * Cache of scan results objects (size is somewhat arbitrary) - */ - private static final int SCAN_RESULT_CACHE_SIZE = 80; - private final LinkedHashMap<String, ScanResult> mScanResultCache; - - /* - * Character buffer used to parse scan results (optimization) - */ - private static final int SCAN_RESULT_BUFFER_SIZE = 512; - private boolean mNeedReconfig; - - /* - * Last UID that asked to enable WIFI. - */ - private int mLastEnableUid = Process.myUid(); - - /* - * Last UID that asked to enable WIFI AP. - */ - private int mLastApEnableUid = Process.myUid(); - - /** * Number of allowed radio frequency channels in various regulatory domains. * This list is sufficient for 802.11b/g networks (2.4GHz range). @@ -191,46 +121,27 @@ public class WifiService extends IWifiManager.Stub { private static final String ACTION_DEVICE_IDLE = "com.android.server.WifiManager.action.DEVICE_IDLE"; + private boolean mIsReceiverRegistered = false; + WifiService(Context context, WifiStateTracker tracker) { mContext = context; mWifiStateTracker = tracker; mWifiStateTracker.enableRssiPolling(true); mBatteryStats = BatteryStatsService.getService(); - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - nwService = INetworkManagementService.Stub.asInterface(b); - - mScanResultCache = new LinkedHashMap<String, ScanResult>( - SCAN_RESULT_CACHE_SIZE, 0.75f, true) { - /* - * Limit the cache size by SCAN_RESULT_CACHE_SIZE - * elements - */ - public boolean removeEldestEntry(Map.Entry eldest) { - return SCAN_RESULT_CACHE_SIZE < this.size(); - } - }; - - HandlerThread wifiThread = new HandlerThread("WifiService"); - wifiThread.start(); - mWifiHandler = new WifiHandler(wifiThread.getLooper()); - - mWifiStateTracker.setWifiState(WIFI_STATE_DISABLED); - mWifiApState = WIFI_AP_STATE_DISABLED; - mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); - PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); + HandlerThread wifiThread = new HandlerThread("WifiService"); + wifiThread.start(); mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // clear our flag indicating the user has overwridden airplane mode - mAirplaneModeOverwridden = false; + mAirplaneModeOverwridden.set(false); // on airplane disable, restore Wifi if the saved state indicates so if (!isAirplaneModeOn() && testAndClearWifiSavedState()) { persistWifiEnabled(true); @@ -245,11 +156,11 @@ public class WifiService extends IWifiManager.Stub { @Override public void onReceive(Context context, Intent intent) { - ArrayList<String> available = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_AVAILABLE_TETHER); - ArrayList<String> active = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_ACTIVE_TETHER); - updateTetherState(available, active); + ArrayList<String> available = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_AVAILABLE_TETHER); + ArrayList<String> active = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_ACTIVE_TETHER); + updateTetherState(available, active); } },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); @@ -301,17 +212,14 @@ public class WifiService extends IWifiManager.Stub { } } catch (Exception e) { Slog.e(TAG, "Error configuring interface " + intf + ", :" + e); - try { - nwService.stopAccessPoint(); - } catch (Exception ee) { - Slog.e(TAG, "Could not stop AP, :" + ee); - } - setWifiApEnabledState(WIFI_AP_STATE_FAILED, 0, DriverAction.DRIVER_UNLOAD); + setWifiApEnabled(null, false); return; } if(mCm.tether(intf) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Slog.e(TAG, "Error tethering "+intf); + Slog.e(TAG, "Error tethering on " + intf); + setWifiApEnabled(null, false); + return; } break; } @@ -353,12 +261,11 @@ public class WifiService extends IWifiManager.Stub { /** * see {@link android.net.wifi.WifiManager#pingSupplicant()} - * @return {@code true} if the operation succeeds + * @return {@code true} if the operation succeeds, {@code false} otherwise */ public boolean pingSupplicant() { - enforceChangePermission(); - - return mWifiStateTracker.ping(); + enforceAccessPermission(); + return mWifiStateTracker.pingSupplicant(); } /** @@ -367,19 +274,24 @@ public class WifiService extends IWifiManager.Stub { */ public boolean startScan(boolean forceActive) { enforceChangePermission(); + return mWifiStateTracker.startScan(forceActive); + } - switch (mWifiStateTracker.getSupplicantState()) { - case DISCONNECTED: - case INACTIVE: - case SCANNING: - case DORMANT: - break; - default: - mWifiStateTracker.setScanResultHandling( - WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); - break; - } - return mWifiStateTracker.scan(forceActive); + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, + "WifiService"); + } + + private void enforceChangePermission() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, + "WifiService"); + + } + + private void enforceMulticastChangePermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, + "WifiService"); } /** @@ -388,168 +300,34 @@ public class WifiService extends IWifiManager.Stub { * @return {@code true} if the enable/disable operation was * started or is already in the queue. */ - public boolean setWifiEnabled(boolean enable) { + public synchronized boolean setWifiEnabled(boolean enable) { enforceChangePermission(); - if (mWifiHandler == null) return false; - - synchronized (mWifiHandler) { - // caller may not have WAKE_LOCK permission - it's not required here - long ident = Binder.clearCallingIdentity(); - sWakeLock.acquire(); - Binder.restoreCallingIdentity(ident); - - mLastEnableUid = Binder.getCallingUid(); - // set a flag if the user is enabling Wifi while in airplane mode - mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable()); - sendEnableMessage(enable, true, Binder.getCallingUid()); - } - - return true; - } - - /** - * Enables/disables Wi-Fi synchronously. - * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off. - * @param persist {@code true} if the setting should be persisted. - * @param uid The UID of the process making the request. - * @return {@code true} if the operation succeeds (or if the existing state - * is the same as the requested state) - */ - private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) { - final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED; - final int wifiState = mWifiStateTracker.getWifiState(); - - if (wifiState == eventualWifiState) { - return true; - } - if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) { - return false; - } - /** - * Multiple calls to unregisterReceiver() cause exception and a system crash. - * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates - * disable wifi at the same time. - * Avoid doing a disable when the current Wifi state is UNKNOWN - * TODO: Handle driver load fail and supplicant lost as seperate states - */ - if ((wifiState == WIFI_STATE_UNKNOWN) && !enable) { - return false; + if (DBG) { + Slog.e(TAG, "Invoking mWifiStateTracker.setWifiEnabled\n"); } - /** - * Fail Wifi if AP is enabled - * TODO: Deprecate WIFI_STATE_UNKNOWN and rename it - * WIFI_STATE_FAILED - */ - if ((mWifiApState == WIFI_AP_STATE_ENABLED) && enable) { - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - return false; + // set a flag if the user is enabling Wifi while in airplane mode + if (enable && isAirplaneModeOn() && isAirplaneToggleable()) { + mAirplaneModeOverwridden.set(true); } - setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid); + mWifiStateTracker.setWifiEnabled(enable); + persistWifiEnabled(enable); if (enable) { - if (!mWifiStateTracker.loadDriver()) { - Slog.e(TAG, "Failed to load Wi-Fi driver."); - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - return false; + if (!mIsReceiverRegistered) { + registerForBroadcasts(); + mIsReceiverRegistered = true; } - if (!mWifiStateTracker.startSupplicant()) { - mWifiStateTracker.unloadDriver(); - Slog.e(TAG, "Failed to start supplicant daemon."); - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - return false; - } - - registerForBroadcasts(); - mWifiStateTracker.startEventLoop(); - - } else { - + } else if (mIsReceiverRegistered){ mContext.unregisterReceiver(mReceiver); - // Remove notification (it will no-op if it isn't visible) - mWifiStateTracker.setNotificationVisible(false, 0, false, 0); - - boolean failedToStopSupplicantOrUnloadDriver = false; - - if (!mWifiStateTracker.stopSupplicant()) { - Slog.e(TAG, "Failed to stop supplicant daemon."); - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - failedToStopSupplicantOrUnloadDriver = true; - } - - /** - * Reset connections and disable interface - * before we unload the driver - */ - mWifiStateTracker.resetConnections(true); - - if (!mWifiStateTracker.unloadDriver()) { - Slog.e(TAG, "Failed to unload Wi-Fi driver."); - if (!failedToStopSupplicantOrUnloadDriver) { - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - failedToStopSupplicantOrUnloadDriver = true; - } - } - - if (failedToStopSupplicantOrUnloadDriver) { - return false; - } + mIsReceiverRegistered = false; } - // Success! - - if (persist) { - persistWifiEnabled(enable); - } - setWifiEnabledState(eventualWifiState, uid); return true; } - private void setWifiEnabledState(int wifiState, int uid) { - final int previousWifiState = mWifiStateTracker.getWifiState(); - - long ident = Binder.clearCallingIdentity(); - try { - if (wifiState == WIFI_STATE_ENABLED) { - mBatteryStats.noteWifiOn(uid); - } else if (wifiState == WIFI_STATE_DISABLED) { - mBatteryStats.noteWifiOff(uid); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } - - // Update state - mWifiStateTracker.setWifiState(wifiState); - - // Broadcast - final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); - intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); - mContext.sendStickyBroadcast(intent); - } - - private void enforceAccessPermission() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, - "WifiService"); - } - - private void enforceChangePermission() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, - "WifiService"); - - } - - private void enforceMulticastChangePermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, - "WifiService"); - } - /** * see {@link WifiManager#getWifiState()} * @return One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -564,62 +342,49 @@ public class WifiService extends IWifiManager.Stub { } /** - * see {@link android.net.wifi.WifiManager#disconnect()} - * @return {@code true} if the operation succeeds - */ - public boolean disconnect() { - enforceChangePermission(); - - return mWifiStateTracker.disconnect(); - } - - /** - * see {@link android.net.wifi.WifiManager#reconnect()} - * @return {@code true} if the operation succeeds - */ - public boolean reconnect() { - enforceChangePermission(); - - return mWifiStateTracker.reconnectCommand(); - } - - /** - * see {@link android.net.wifi.WifiManager#reassociate()} - * @return {@code true} if the operation succeeds - */ - public boolean reassociate() { - enforceChangePermission(); - - return mWifiStateTracker.reassociate(); - } - - /** * see {@link android.net.wifi.WifiManager#setWifiApEnabled(WifiConfiguration, boolean)} * @param wifiConfig SSID, security and channel details as * part of WifiConfiguration - * @param enabled, true to enable and false to disable + * @param enabled true to enable and false to disable * @return {@code true} if the start operation was * started or is already in the queue. */ - public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { + public synchronized boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { enforceChangePermission(); - if (mWifiHandler == null) return false; - - synchronized (mWifiHandler) { - - long ident = Binder.clearCallingIdentity(); - sWakeLock.acquire(); - Binder.restoreCallingIdentity(ident); - mLastApEnableUid = Binder.getCallingUid(); - sendAccessPointMessage(enabled, wifiConfig, Binder.getCallingUid()); + if (enabled) { + /* Use default config if there is no existing config */ + if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) { + wifiConfig = new WifiConfiguration(); + wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); + wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); + } + setWifiApConfiguration(wifiConfig); } + mWifiStateTracker.setWifiApEnabled(wifiConfig, enabled); + return true; } - public WifiConfiguration getWifiApConfiguration() { + /** + * see {@link WifiManager#getWifiApState()} + * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + */ + public int getWifiApEnabledState() { enforceAccessPermission(); + return mWifiStateTracker.getWifiApState(); + } + + /** + * see {@link WifiManager#getWifiApConfiguration()} + * @return soft access point configuration + */ + public synchronized WifiConfiguration getWifiApConfiguration() { final ContentResolver cr = mContext.getContentResolver(); WifiConfiguration wifiConfig = new WifiConfiguration(); int authType; @@ -637,7 +402,11 @@ public class WifiService extends IWifiManager.Stub { } } - public void setWifiApConfiguration(WifiConfiguration wifiConfig) { + /** + * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)} + * @param wifiConfig WifiConfiguration details for soft access point + */ + public synchronized void setWifiApConfiguration(WifiConfiguration wifiConfig) { enforceChangePermission(); final ContentResolver cr = mContext.getContentResolver(); boolean isWpa; @@ -653,143 +422,30 @@ public class WifiService extends IWifiManager.Stub { } /** - * Enables/disables Wi-Fi AP synchronously. The driver is loaded - * and soft access point configured as a single operation. - * @param enable {@code true} to turn Wi-Fi on, {@code false} to turn it off. - * @param uid The UID of the process making the request. - * @param wifiConfig The WifiConfiguration for AP - * @return {@code true} if the operation succeeds (or if the existing state - * is the same as the requested state) + * see {@link android.net.wifi.WifiManager#disconnect()} + * @return {@code true} if the operation succeeds */ - private boolean setWifiApEnabledBlocking(boolean enable, - int uid, WifiConfiguration wifiConfig) { - final int eventualWifiApState = enable ? WIFI_AP_STATE_ENABLED : WIFI_AP_STATE_DISABLED; - - if (mWifiApState == eventualWifiApState) { - /* Configuration changed on a running access point */ - if(enable && (wifiConfig != null)) { - try { - nwService.setAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(), - SOFTAP_IFACE); - setWifiApConfiguration(wifiConfig); - return true; - } catch(Exception e) { - Slog.e(TAG, "Exception in nwService during AP restart"); - try { - nwService.stopAccessPoint(); - } catch (Exception ee) { - Slog.e(TAG, "Could not stop AP, :" + ee); - } - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD); - return false; - } - } else { - return true; - } - } - - /** - * Fail AP if Wifi is enabled - */ - if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLED) && enable) { - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD); - return false; - } - - setWifiApEnabledState(enable ? WIFI_AP_STATE_ENABLING : - WIFI_AP_STATE_DISABLING, uid, DriverAction.NO_DRIVER_UNLOAD); - - if (enable) { - - /* Use default config if there is no existing config */ - if (wifiConfig == null && ((wifiConfig = getWifiApConfiguration()) == null)) { - wifiConfig = new WifiConfiguration(); - wifiConfig.SSID = mContext.getString(R.string.wifi_tether_configure_ssid_default); - wifiConfig.allowedKeyManagement.set(KeyMgmt.NONE); - } - - if (!mWifiStateTracker.loadDriver()) { - Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode"); - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD); - return false; - } - - try { - nwService.startAccessPoint(wifiConfig, mWifiStateTracker.getInterfaceName(), - SOFTAP_IFACE); - } catch(Exception e) { - Slog.e(TAG, "Exception in startAccessPoint()"); - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD); - return false; - } - - setWifiApConfiguration(wifiConfig); - - } else { - - try { - nwService.stopAccessPoint(); - } catch(Exception e) { - Slog.e(TAG, "Exception in stopAccessPoint()"); - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.DRIVER_UNLOAD); - return false; - } - - if (!mWifiStateTracker.unloadDriver()) { - Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode"); - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid, DriverAction.NO_DRIVER_UNLOAD); - return false; - } - } - - setWifiApEnabledState(eventualWifiApState, uid, DriverAction.NO_DRIVER_UNLOAD); - return true; + public boolean disconnect() { + enforceChangePermission(); + return mWifiStateTracker.disconnectCommand(); } /** - * see {@link WifiManager#getWifiApState()} - * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, - * {@link WifiManager#WIFI_AP_STATE_DISABLING}, - * {@link WifiManager#WIFI_AP_STATE_ENABLED}, - * {@link WifiManager#WIFI_AP_STATE_ENABLING}, - * {@link WifiManager#WIFI_AP_STATE_FAILED} + * see {@link android.net.wifi.WifiManager#reconnect()} + * @return {@code true} if the operation succeeds */ - public int getWifiApEnabledState() { - enforceAccessPermission(); - return mWifiApState; + public boolean reconnect() { + enforceChangePermission(); + return mWifiStateTracker.reconnectCommand(); } - private void setWifiApEnabledState(int wifiAPState, int uid, DriverAction flag) { - final int previousWifiApState = mWifiApState; - - /** - * Unload the driver if going to a failed state - */ - if ((mWifiApState == WIFI_AP_STATE_FAILED) && (flag == DriverAction.DRIVER_UNLOAD)) { - mWifiStateTracker.unloadDriver(); - } - - long ident = Binder.clearCallingIdentity(); - try { - if (wifiAPState == WIFI_AP_STATE_ENABLED) { - mBatteryStats.noteWifiOn(uid); - } else if (wifiAPState == WIFI_AP_STATE_DISABLED) { - mBatteryStats.noteWifiOff(uid); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } - - // Update state - mWifiApState = wifiAPState; - - // Broadcast - final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiAPState); - intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); - mContext.sendStickyBroadcast(intent); + /** + * see {@link android.net.wifi.WifiManager#reassociate()} + * @return {@code true} if the operation succeeds + */ + public boolean reassociate() { + enforceChangePermission(); + return mWifiStateTracker.reassociateCommand(); } /** @@ -798,217 +454,7 @@ public class WifiService extends IWifiManager.Stub { */ public List<WifiConfiguration> getConfiguredNetworks() { enforceAccessPermission(); - String listStr; - - /* - * We don't cache the list, because we want to allow - * for the possibility that the configuration file - * has been modified through some external means, - * such as the wpa_cli command line program. - */ - listStr = mWifiStateTracker.listNetworks(); - - List<WifiConfiguration> networks = - new ArrayList<WifiConfiguration>(); - if (listStr == null) - return networks; - - String[] lines = listStr.split("\n"); - // Skip the first line, which is a header - for (int i = 1; i < lines.length; i++) { - String[] result = lines[i].split("\t"); - // network-id | ssid | bssid | flags - WifiConfiguration config = new WifiConfiguration(); - try { - config.networkId = Integer.parseInt(result[0]); - } catch(NumberFormatException e) { - continue; - } - if (result.length > 3) { - if (result[3].indexOf("[CURRENT]") != -1) - config.status = WifiConfiguration.Status.CURRENT; - else if (result[3].indexOf("[DISABLED]") != -1) - config.status = WifiConfiguration.Status.DISABLED; - else - config.status = WifiConfiguration.Status.ENABLED; - } else { - config.status = WifiConfiguration.Status.ENABLED; - } - readNetworkVariables(config); - networks.add(config); - } - - return networks; - } - - /** - * Read the variables from the supplicant daemon that are needed to - * fill in the WifiConfiguration object. - * <p/> - * The caller must hold the synchronization monitor. - * @param config the {@link WifiConfiguration} object to be filled in. - */ - private void readNetworkVariables(WifiConfiguration config) { - - int netId = config.networkId; - if (netId < 0) - return; - - /* - * TODO: maybe should have a native method that takes an array of - * variable names and returns an array of values. But we'd still - * be doing a round trip to the supplicant daemon for each variable. - */ - String value; - - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.ssidVarName); - if (!TextUtils.isEmpty(value)) { - config.SSID = value; - } else { - config.SSID = null; - } - - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.bssidVarName); - if (!TextUtils.isEmpty(value)) { - config.BSSID = value; - } else { - config.BSSID = null; - } - - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.priorityVarName); - config.priority = -1; - if (!TextUtils.isEmpty(value)) { - try { - config.priority = Integer.parseInt(value); - } catch (NumberFormatException ignore) { - } - } - - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); - config.hiddenSSID = false; - if (!TextUtils.isEmpty(value)) { - try { - config.hiddenSSID = Integer.parseInt(value) != 0; - } catch (NumberFormatException ignore) { - } - } - - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); - config.wepTxKeyIndex = -1; - if (!TextUtils.isEmpty(value)) { - try { - config.wepTxKeyIndex = Integer.parseInt(value); - } catch (NumberFormatException ignore) { - } - } - - /* - * Get up to 4 WEP keys. Note that the actual keys are not passed back, - * just a "*" if the key is set, or the null string otherwise. - */ - for (int i = 0; i < 4; i++) { - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepKeyVarNames[i]); - if (!TextUtils.isEmpty(value)) { - config.wepKeys[i] = value; - } else { - config.wepKeys[i] = null; - } - } - - /* - * Get the private shared key. Note that the actual keys are not passed back, - * just a "*" if the key is set, or the null string otherwise. - */ - value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.pskVarName); - if (!TextUtils.isEmpty(value)) { - config.preSharedKey = value; - } else { - config.preSharedKey = null; - } - - value = mWifiStateTracker.getNetworkVariable(config.networkId, - WifiConfiguration.Protocol.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.Protocol.strings); - if (0 <= index) { - config.allowedProtocols.set(index); - } - } - } - - value = mWifiStateTracker.getNetworkVariable(config.networkId, - WifiConfiguration.KeyMgmt.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.KeyMgmt.strings); - if (0 <= index) { - config.allowedKeyManagement.set(index); - } - } - } - - value = mWifiStateTracker.getNetworkVariable(config.networkId, - WifiConfiguration.AuthAlgorithm.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.AuthAlgorithm.strings); - if (0 <= index) { - config.allowedAuthAlgorithms.set(index); - } - } - } - - value = mWifiStateTracker.getNetworkVariable(config.networkId, - WifiConfiguration.PairwiseCipher.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.PairwiseCipher.strings); - if (0 <= index) { - config.allowedPairwiseCiphers.set(index); - } - } - } - - value = mWifiStateTracker.getNetworkVariable(config.networkId, - WifiConfiguration.GroupCipher.varName); - if (!TextUtils.isEmpty(value)) { - String vals[] = value.split(" "); - for (String val : vals) { - int index = - lookupString(val, WifiConfiguration.GroupCipher.strings); - if (0 <= index) { - config.allowedGroupCiphers.set(index); - } - } - } - - for (WifiConfiguration.EnterpriseField field : - config.enterpriseFields) { - value = mWifiStateTracker.getNetworkVariable(netId, - field.varName()); - if (!TextUtils.isEmpty(value)) { - if (field != config.eap) value = removeDoubleQuotes(value); - field.setValue(value); - } - } - } - - private static String removeDoubleQuotes(String string) { - if (string.length() <= 2) return ""; - return string.substring(1, string.length() - 1); - } - - private static String convertToQuotedString(String string) { - return "\"" + string + "\""; + return mWifiStateTracker.getConfiguredNetworks(); } /** @@ -1018,280 +464,10 @@ public class WifiService extends IWifiManager.Stub { */ public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); - - /* - * If the supplied networkId is -1, we create a new empty - * network configuration. Otherwise, the networkId should - * refer to an existing configuration. - */ - int netId = config.networkId; - boolean newNetwork = netId == -1; - boolean doReconfig = false; - // networkId of -1 means we want to create a new network - synchronized (mWifiStateTracker) { - if (newNetwork) { - netId = mWifiStateTracker.addNetwork(); - if (netId < 0) { - if (DBG) { - Slog.d(TAG, "Failed to add a network!"); - } - return -1; - } - doReconfig = true; - } - mNeedReconfig = mNeedReconfig || doReconfig; - } - - setVariables: { - /* - * Note that if a networkId for a non-existent network - * was supplied, then the first setNetworkVariable() - * will fail, so we don't bother to make a separate check - * for the validity of the ID up front. - */ - if (config.SSID != null && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.ssidVarName, - config.SSID)) { - if (DBG) { - Slog.d(TAG, "failed to set SSID: "+config.SSID); - } - break setVariables; - } - - if (config.BSSID != null && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.bssidVarName, - config.BSSID)) { - if (DBG) { - Slog.d(TAG, "failed to set BSSID: "+config.BSSID); - } - break setVariables; - } - - String allowedKeyManagementString = - makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); - if (config.allowedKeyManagement.cardinality() != 0 && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.KeyMgmt.varName, - allowedKeyManagementString)) { - if (DBG) { - Slog.d(TAG, "failed to set key_mgmt: "+ - allowedKeyManagementString); - } - break setVariables; - } - - String allowedProtocolsString = - makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); - if (config.allowedProtocols.cardinality() != 0 && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.Protocol.varName, - allowedProtocolsString)) { - if (DBG) { - Slog.d(TAG, "failed to set proto: "+ - allowedProtocolsString); - } - break setVariables; - } - - String allowedAuthAlgorithmsString = - makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); - if (config.allowedAuthAlgorithms.cardinality() != 0 && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.AuthAlgorithm.varName, - allowedAuthAlgorithmsString)) { - if (DBG) { - Slog.d(TAG, "failed to set auth_alg: "+ - allowedAuthAlgorithmsString); - } - break setVariables; - } - - String allowedPairwiseCiphersString = - makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings); - if (config.allowedPairwiseCiphers.cardinality() != 0 && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.PairwiseCipher.varName, - allowedPairwiseCiphersString)) { - if (DBG) { - Slog.d(TAG, "failed to set pairwise: "+ - allowedPairwiseCiphersString); - } - break setVariables; - } - - String allowedGroupCiphersString = - makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); - if (config.allowedGroupCiphers.cardinality() != 0 && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.GroupCipher.varName, - allowedGroupCiphersString)) { - if (DBG) { - Slog.d(TAG, "failed to set group: "+ - allowedGroupCiphersString); - } - break setVariables; - } - - // Prevent client screw-up by passing in a WifiConfiguration we gave it - // by preventing "*" as a key. - if (config.preSharedKey != null && !config.preSharedKey.equals("*") && - !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.pskVarName, - config.preSharedKey)) { - if (DBG) { - Slog.d(TAG, "failed to set psk: "+config.preSharedKey); - } - break setVariables; - } - - boolean hasSetKey = false; - if (config.wepKeys != null) { - for (int i = 0; i < config.wepKeys.length; i++) { - // Prevent client screw-up by passing in a WifiConfiguration we gave it - // by preventing "*" as a key. - if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { - if (!mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.wepKeyVarNames[i], - config.wepKeys[i])) { - if (DBG) { - Slog.d(TAG, - "failed to set wep_key"+i+": " + - config.wepKeys[i]); - } - break setVariables; - } - hasSetKey = true; - } - } - } - - if (hasSetKey) { - if (!mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.wepTxKeyIdxVarName, - Integer.toString(config.wepTxKeyIndex))) { - if (DBG) { - Slog.d(TAG, - "failed to set wep_tx_keyidx: "+ - config.wepTxKeyIndex); - } - break setVariables; - } - } - - if (!mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.priorityVarName, - Integer.toString(config.priority))) { - if (DBG) { - Slog.d(TAG, config.SSID + ": failed to set priority: " - +config.priority); - } - break setVariables; - } - - if (config.hiddenSSID && !mWifiStateTracker.setNetworkVariable( - netId, - WifiConfiguration.hiddenSSIDVarName, - Integer.toString(config.hiddenSSID ? 1 : 0))) { - if (DBG) { - Slog.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ - config.hiddenSSID); - } - break setVariables; - } - - for (WifiConfiguration.EnterpriseField field - : config.enterpriseFields) { - String varName = field.varName(); - String value = field.value(); - if (value != null) { - if (field != config.eap) { - value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); - } - if (!mWifiStateTracker.setNetworkVariable( - netId, - varName, - value)) { - if (DBG) { - Slog.d(TAG, config.SSID + ": failed to set " + varName + - ": " + value); - } - break setVariables; - } - } - } - return netId; - } - - /* - * For an update, if one of the setNetworkVariable operations fails, - * we might want to roll back all the changes already made. But the - * chances are that if anything is going to go wrong, it'll happen - * the first time we try to set one of the variables. - */ - if (newNetwork) { - removeNetwork(netId); - if (DBG) { - Slog.d(TAG, - "Failed to set a network variable, removed network: " - + netId); - } - } - return -1; + return mWifiStateTracker.addOrUpdateNetwork(config); } - private static String makeString(BitSet set, String[] strings) { - StringBuffer buf = new StringBuffer(); - int nextSetBit = -1; - - /* Make sure all set bits are in [0, strings.length) to avoid - * going out of bounds on strings. (Shouldn't happen, but...) */ - set = set.get(0, strings.length); - - while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { - buf.append(strings[nextSetBit].replace('_', '-')).append(' '); - } - - // remove trailing space - if (set.cardinality() > 0) { - buf.setLength(buf.length() - 1); - } - - return buf.toString(); - } - - private static int lookupString(String string, String[] strings) { - int size = strings.length; - - string = string.replace('-', '_'); - - for (int i = 0; i < size; i++) - if (string.equals(strings[i])) - return i; - - if (DBG) { - // if we ever get here, we should probably add the - // value to WifiConfiguration to reflect that it's - // supported by the WPA supplicant - Slog.w(TAG, "Failed to look-up a string: " + string); - } - - return -1; - } - - /** + /** * See {@link android.net.wifi.WifiManager#removeNetwork(int)} * @param netId the integer that identifies the network configuration * to the supplicant @@ -1299,7 +475,6 @@ public class WifiService extends IWifiManager.Stub { */ public boolean removeNetwork(int netId) { enforceChangePermission(); - return mWifiStateTracker.removeNetwork(netId); } @@ -1312,14 +487,7 @@ public class WifiService extends IWifiManager.Stub { */ public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); - - String ifname = mWifiStateTracker.getInterfaceName(); - NetworkUtils.enableInterface(ifname); - boolean result = mWifiStateTracker.enableNetwork(netId, disableOthers); - if (!result) { - NetworkUtils.disableInterface(ifname); - } - return result; + return mWifiStateTracker.enableNetwork(netId, disableOthers); } /** @@ -1330,7 +498,6 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disableNetwork(int netId) { enforceChangePermission(); - return mWifiStateTracker.disableNetwork(netId); } @@ -1354,180 +521,19 @@ public class WifiService extends IWifiManager.Stub { */ public List<ScanResult> getScanResults() { enforceAccessPermission(); - String reply; - - reply = mWifiStateTracker.scanResults(); - if (reply == null) { - return null; - } - - List<ScanResult> scanList = new ArrayList<ScanResult>(); - - int lineCount = 0; - - int replyLen = reply.length(); - // Parse the result string, keeping in mind that the last line does - // not end with a newline. - for (int lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) { - if (lineEnd == replyLen || reply.charAt(lineEnd) == '\n') { - ++lineCount; - /* - * Skip the first line, which is a header - */ - if (lineCount == 1) { - lineBeg = lineEnd + 1; - continue; - } - if (lineEnd > lineBeg) { - String line = reply.substring(lineBeg, lineEnd); - ScanResult scanResult = parseScanResult(line); - if (scanResult != null) { - scanList.add(scanResult); - } else if (DBG) { - Slog.w(TAG, "misformatted scan result for: " + line); - } - } - lineBeg = lineEnd + 1; - } - } - mWifiStateTracker.setScanResultsList(scanList); - return scanList; - } - - /** - * Parse the scan result line passed to us by wpa_supplicant (helper). - * @param line the line to parse - * @return the {@link ScanResult} object - */ - private ScanResult parseScanResult(String line) { - ScanResult scanResult = null; - if (line != null) { - /* - * Cache implementation (LinkedHashMap) is not synchronized, thus, - * must synchronized here! - */ - synchronized (mScanResultCache) { - String[] result = scanResultPattern.split(line); - if (3 <= result.length && result.length <= 5) { - String bssid = result[0]; - // bssid | frequency | level | flags | ssid - int frequency; - int level; - try { - frequency = Integer.parseInt(result[1]); - level = Integer.parseInt(result[2]); - /* some implementations avoid negative values by adding 256 - * so we need to adjust for that here. - */ - if (level > 0) level -= 256; - } catch (NumberFormatException e) { - frequency = 0; - level = 0; - } - - /* - * The formatting of the results returned by - * wpa_supplicant is intended to make the fields - * line up nicely when printed, - * not to make them easy to parse. So we have to - * apply some heuristics to figure out which field - * is the SSID and which field is the flags. - */ - String ssid; - String flags; - if (result.length == 4) { - if (result[3].charAt(0) == '[') { - flags = result[3]; - ssid = ""; - } else { - flags = ""; - ssid = result[3]; - } - } else if (result.length == 5) { - flags = result[3]; - ssid = result[4]; - } else { - // Here, we must have 3 fields: no flags and ssid - // set - flags = ""; - ssid = ""; - } - - // bssid + ssid is the hash key - String key = bssid + ssid; - scanResult = mScanResultCache.get(key); - if (scanResult != null) { - scanResult.level = level; - scanResult.SSID = ssid; - scanResult.capabilities = flags; - scanResult.frequency = frequency; - } else { - // Do not add scan results that have no SSID set - if (0 < ssid.trim().length()) { - scanResult = - new ScanResult( - ssid, bssid, flags, level, frequency); - mScanResultCache.put(key, scanResult); - } - } - } else { - Slog.w(TAG, "Misformatted scan result text with " + - result.length + " fields: " + line); - } - } - } - - return scanResult; - } - - /** - * Parse the "flags" field passed back in a scan result by wpa_supplicant, - * and construct a {@code WifiConfiguration} that describes the encryption, - * key management, and authenticaion capabilities of the access point. - * @param flags the string returned by wpa_supplicant - * @return the {@link WifiConfiguration} object, filled in - */ - WifiConfiguration parseScanFlags(String flags) { - WifiConfiguration config = new WifiConfiguration(); - - if (flags.length() == 0) { - config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); - } - // ... to be implemented - return config; + return mWifiStateTracker.getScanResultsList(); } /** * Tell the supplicant to persist the current list of configured networks. * @return {@code true} if the operation succeeded + * + * TODO: deprecate this */ public boolean saveConfiguration() { - boolean result; + boolean result = true; enforceChangePermission(); - - synchronized (mWifiStateTracker) { - result = mWifiStateTracker.saveConfig(); - if (result && mNeedReconfig) { - mNeedReconfig = false; - result = mWifiStateTracker.reloadConfig(); - - if (result) { - Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION); - mContext.sendBroadcast(intent); - } - } - } - // Inform the backup manager about a data change - IBackupManager ibm = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - if (ibm != null) { - try { - ibm.dataChanged("com.android.providers.settings"); - } catch (Exception e) { - // Try again later - } - } - return result; + return mWifiStateTracker.saveConfig(); } /** @@ -1542,7 +548,7 @@ public class WifiService extends IWifiManager.Stub { * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., * {@code numChannels} is outside the valid range. */ - public boolean setNumAllowedChannels(int numChannels, boolean persist) { + public synchronized boolean setNumAllowedChannels(int numChannels, boolean persist) { Slog.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+ " with persist set to "+persist); enforceChangePermission(); @@ -1564,28 +570,15 @@ public class WifiService extends IWifiManager.Stub { return false; } - if (mWifiHandler == null) return false; - - Message.obtain(mWifiHandler, - MESSAGE_SET_CHANNELS, numChannels, (persist ? 1 : 0)).sendToTarget(); - - return true; - } - - /** - * sets the number of allowed radio frequency channels synchronously - * @param numChannels the number of allowed channels. Must be greater than 0 - * and less than or equal to 16. - * @param persist {@code true} if the setting should be remembered. - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - private boolean setNumAllowedChannelsBlocking(int numChannels, boolean persist) { if (persist) { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, numChannels); } - return mWifiStateTracker.setNumAllowedChannels(numChannels); + + mWifiStateTracker.setNumAllowedChannels(numChannels); + + return true; } /** @@ -1663,8 +656,8 @@ public class WifiService extends IWifiManager.Stub { WifiInfo info = mWifiStateTracker.requestConnectionInfo(); if (info.getSupplicantState() != SupplicantState.COMPLETED) { // we used to go to sleep immediately, but this caused some race conditions - // we don't have time to track down for this release. Delay instead, but not - // as long as we would if connected (below) + // we don't have time to track down for this release. Delay instead, + // but not as long as we would if connected (below) // TODO - fix the race conditions and switch back to the immediate turn-off long triggerTime = System.currentTimeMillis() + (2*60*1000); // 2 min Slog.d(TAG, "setting ACTION_DEVICE_IDLE timer for 120,000 ms"); @@ -1763,31 +756,9 @@ public class WifiService extends IWifiManager.Stub { } }; - private void sendEnableMessage(boolean enable, boolean persist, int uid) { - Message msg = Message.obtain(mWifiHandler, - (enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI), - (persist ? 1 : 0), uid); - msg.sendToTarget(); - } - - private void sendStartMessage(boolean scanOnlyMode) { - Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget(); - } - - private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) { - Message.obtain(mWifiHandler, - (enable ? MESSAGE_START_ACCESS_POINT : MESSAGE_STOP_ACCESS_POINT), - uid, 0, wifiConfig).sendToTarget(); - } - private void updateWifiState() { - // send a message so it's all serialized - Message.obtain(mWifiHandler, MESSAGE_UPDATE_STATE, 0, 0).sendToTarget(); - } - - private void doUpdateWifiState() { boolean wifiEnabled = getPersistedWifiEnabled(); - boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden; + boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden.get(); boolean lockHeld = mLocks.hasLocks(); int strongestLockMode; boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode; @@ -1798,36 +769,23 @@ public class WifiService extends IWifiManager.Stub { strongestLockMode = WifiManager.WIFI_MODE_FULL; } - synchronized (mWifiHandler) { - if ((mWifiStateTracker.getWifiState() == WIFI_STATE_ENABLING) && !airplaneMode) { - return; - } - - /* Disable tethering when airplane mode is enabled */ - if (airplaneMode && - (mWifiApState == WIFI_AP_STATE_ENABLING || mWifiApState == WIFI_AP_STATE_ENABLED)) { - sWakeLock.acquire(); - sendAccessPointMessage(false, null, mLastApEnableUid); - } + /* Disable tethering when airplane mode is enabled */ + if (airplaneMode) { + mWifiStateTracker.setWifiApEnabled(null, false); + } - if (wifiShouldBeEnabled) { - if (wifiShouldBeStarted) { - sWakeLock.acquire(); - sendEnableMessage(true, false, mLastEnableUid); - sWakeLock.acquire(); - sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); - } else if (!mWifiStateTracker.isDriverStopped()) { - if (mCm == null) { - mCm = (ConnectivityManager)mContext. - getSystemService(Context.CONNECTIVITY_SERVICE); - } - mCm.requestNetworkTransitionWakelock(TAG); - mWifiHandler.sendEmptyMessage(MESSAGE_STOP_WIFI); - } + if (wifiShouldBeEnabled) { + if (wifiShouldBeStarted) { + mWifiStateTracker.setWifiEnabled(true); + mWifiStateTracker.setScanOnlyMode( + strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); + mWifiStateTracker.startWifi(true); } else { - sWakeLock.acquire(); - sendEnableMessage(false, false, mLastEnableUid); + mWifiStateTracker.requestCmWakeLock(); + mWifiStateTracker.disconnectAndStop(); } + } else { + mWifiStateTracker.setWifiEnabled(false); } } @@ -1865,71 +823,6 @@ public class WifiService extends IWifiManager.Stub { Settings.System.AIRPLANE_MODE_ON, 0) == 1; } - /** - * Handler that allows posting to the WifiThread. - */ - private class WifiHandler extends Handler { - public WifiHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - - case MESSAGE_ENABLE_WIFI: - setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2); - if (mWifiWatchdogService == null) { - mWifiWatchdogService = new WifiWatchdogService(mContext, mWifiStateTracker); - } - sWakeLock.release(); - break; - - case MESSAGE_START_WIFI: - mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0); - mWifiStateTracker.restart(); - sWakeLock.release(); - break; - - case MESSAGE_UPDATE_STATE: - doUpdateWifiState(); - break; - - case MESSAGE_DISABLE_WIFI: - // a non-zero msg.arg1 value means the "enabled" setting - // should be persisted - setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2); - mWifiWatchdogService = null; - sWakeLock.release(); - break; - - case MESSAGE_STOP_WIFI: - mWifiStateTracker.disconnectAndStop(); - // don't release wakelock - break; - - case MESSAGE_START_ACCESS_POINT: - setWifiApEnabledBlocking(true, - msg.arg1, - (WifiConfiguration) msg.obj); - sWakeLock.release(); - break; - - case MESSAGE_STOP_ACCESS_POINT: - setWifiApEnabledBlocking(false, - msg.arg1, - (WifiConfiguration) msg.obj); - sWakeLock.release(); - break; - - case MESSAGE_SET_CHANNELS: - setNumAllowedChannelsBlocking(msg.arg1, msg.arg2 == 1); - break; - - } - } - } - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -1939,7 +832,7 @@ public class WifiService extends IWifiManager.Stub { + ", uid=" + Binder.getCallingUid()); return; } - pw.println("Wi-Fi is " + stateName(mWifiStateTracker.getWifiState())); + pw.println("Wi-Fi is " + mWifiStateTracker.getWifiStateByName()); pw.println("Stay-awake conditions: " + Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0)); @@ -1971,23 +864,6 @@ public class WifiService extends IWifiManager.Stub { mLocks.dump(pw); } - private static String stateName(int wifiState) { - switch (wifiState) { - case WIFI_STATE_DISABLING: - return "disabling"; - case WIFI_STATE_DISABLED: - return "disabled"; - case WIFI_STATE_ENABLING: - return "enabling"; - case WIFI_STATE_ENABLED: - return "enabled"; - case WIFI_STATE_UNKNOWN: - return "unknown state"; - default: - return "[invalid state]"; - } - } - private class WifiLock extends DeathRecipient { WifiLock(int lockMode, String tag, IBinder binder) { super(lockMode, tag, binder); diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 445dd0366d57..be14cd3d70f1 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -275,12 +275,13 @@ public class WifiWatchdogService { /** * Unregister broadcasts and quit the watchdog thread */ - private void quit() { - unregisterForWifiBroadcasts(); - mContext.getContentResolver().unregisterContentObserver(mContentObserver); - mHandler.removeAllActions(); - mHandler.getLooper().quit(); - } + //TODO: Change back to running WWS when needed +// private void quit() { +// unregisterForWifiBroadcasts(); +// mContext.getContentResolver().unregisterContentObserver(mContentObserver); +// mHandler.removeAllActions(); +// mHandler.getLooper().quit(); +// } /** * Waits for the main watchdog thread to create the handler. @@ -751,7 +752,7 @@ public class WifiWatchdogService { // Black list this "bad" AP, this will cause an attempt to connect to another blacklistAp(ap.bssid); // Initiate an association to an alternate AP - mWifiStateTracker.reassociate(); + mWifiStateTracker.reassociateCommand(); } private void blacklistAp(String bssid) { @@ -762,10 +763,7 @@ public class WifiWatchdogService { // Before taking action, make sure we should not cancel our processing if (shouldCancel()) return; - if (!mWifiStateTracker.addToBlacklist(bssid)) { - // There's a known bug where this method returns failure on success - //Slog.e(TAG, "Blacklisting " + bssid + " failed"); - } + mWifiStateTracker.addToBlacklist(bssid); if (D) { myLogD("Blacklisting " + bssid); @@ -860,10 +858,7 @@ public class WifiWatchdogService { * (and blacklisted them). Clear the blacklist so the AP with best * signal is chosen. */ - if (!mWifiStateTracker.clearBlacklist()) { - // There's a known bug where this method returns failure on success - //Slog.e(TAG, "Clearing blacklist failed"); - } + mWifiStateTracker.clearBlacklist(); if (V) { myLogV("handleSleep: Set state to SLEEP and cleared blacklist"); @@ -1151,7 +1146,7 @@ public class WifiWatchdogService { private void handleWifiStateChanged(int wifiState) { if (wifiState == WifiManager.WIFI_STATE_DISABLED) { - quit(); + onDisconnected(); } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) { onEnabled(); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index fa2ec1fd1327..440ebbd84c85 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -6173,7 +6173,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; boolean bufferWasEmpty; - + boolean needsFlush; final StringBuilder sb = isSystemApp ? mStrictModeBuffer : new StringBuilder(1024); synchronized (sb) { bufferWasEmpty = sb.length() == 0; @@ -6188,18 +6188,32 @@ public final class ActivityManagerService extends ActivityManagerNative implemen sb.append(crashInfo.stackTrace); } sb.append("\n"); + + // Only buffer up to ~64k. Various logging bits truncate + // things at 128k. + needsFlush = (sb.length() > 64 * 1024); } - // Non-system apps are isolated with a different tag & policy. - // They're also not batched. Batching is useful during system - // boot with strict system-wide logging policies and lots of - // things firing, but not common with regular apps, which - // won't ship with StrictMode dropboxing enabled. - if (!isSystemApp) { + // Flush immediately if the buffer's grown too large, or this + // is a non-system app. Non-system apps are isolated with a + // different tag & policy and not batched. + // + // Batching is useful during internal testing with + // StrictMode settings turned up high. Without batching, + // thousands of separate files could be created on boot. + if (!isSystemApp || needsFlush) { new Thread("Error dump: " + dropboxTag) { @Override public void run() { - dbox.addText(dropboxTag, sb.toString()); + String report; + synchronized (sb) { + report = sb.toString(); + sb.delete(0, sb.length()); + sb.trimToSize(); + } + if (report.length() != 0) { + dbox.addText(dropboxTag, report); + } } }.start(); return; @@ -6207,8 +6221,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // System app batching: if (!bufferWasEmpty) { - // An existing dropbox-writing thread is outstanding and - // will handle it. + // An existing dropbox-writing thread is outstanding, so + // we don't need to start it up. The existing thread will + // catch the buffer appends we just did. return; } diff --git a/test-runner/src/android/test/LoaderTestCase.java b/test-runner/src/android/test/LoaderTestCase.java new file mode 100644 index 000000000000..8be6590402a3 --- /dev/null +++ b/test-runner/src/android/test/LoaderTestCase.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.test; + +import android.content.Loader; +import android.content.Loader.OnLoadCompleteListener; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import java.util.concurrent.ArrayBlockingQueue; + +/** + * A convenience class for testing {@link Loader}s. This test case + * provides a simple way to synchronously get the result from a Loader making + * it easy to assert that the Loader returns the expected result. + */ +public class LoaderTestCase extends AndroidTestCase { + static { + // Force class loading of AsyncTask on the main thread so that it's handlers are tied to + // the main thread and responses from the worker thread get delivered on the main thread. + // The tests are run on another thread, allowing them to block waiting on a response from + // the code running on the main thread. The main thread can't block since the AysncTask + // results come in via the event loop. + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... args) {return null;} + @Override + protected void onPostExecute(Void result) {} + }; + } + + /** + * Runs a Loader synchronously and returns the result of the load. The loader will + * be started, stopped, and destroyed by this method so it cannot be reused. + * + * @param loader The loader to run synchronously + * @return The result from the loader + */ + public <T> T getLoaderResultSynchronously(final Loader<T> loader) { + // The test thread blocks on this queue until the loader puts it's result in + final ArrayBlockingQueue<T> queue = new ArrayBlockingQueue<T>(1); + + // This callback runs on the "main" thread and unblocks the test thread + // when it puts the result into the blocking queue + final OnLoadCompleteListener<T> listener = new OnLoadCompleteListener<T>() { + @Override + public void onLoadComplete(Loader<T> completedLoader, T data) { + // Shut the loader down + completedLoader.unregisterListener(this); + completedLoader.stopLoading(); + completedLoader.destroy(); + + // Store the result, unblocking the test thread + queue.add(data); + } + }; + + // This handler runs on the "main" thread of the process since AsyncTask + // is documented as needing to run on the main thread and many Loaders use + // AsyncTask + final Handler mainThreadHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + loader.registerListener(0, listener); + loader.startLoading(); + } + }; + + // Ask the main thread to start the loading process + mainThreadHandler.sendEmptyMessage(0); + + // Block on the queue waiting for the result of the load to be inserted + T result; + while (true) { + try { + result = queue.take(); + break; + } catch (InterruptedException e) { + throw new RuntimeException("waiting thread interrupted", e); + } + } + + return result; + } +} diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 4a22b6885203..6fac902bdd0a 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -165,7 +165,7 @@ public class WifiManager { * * @hide */ - public static final int WIFI_AP_STATE_DISABLING = 0; + public static final int WIFI_AP_STATE_DISABLING = 10; /** * Wi-Fi AP is disabled. * @@ -174,7 +174,7 @@ public class WifiManager { * * @hide */ - public static final int WIFI_AP_STATE_DISABLED = 1; + public static final int WIFI_AP_STATE_DISABLED = 11; /** * Wi-Fi AP is currently being enabled. The state will change to * {@link #WIFI_AP_STATE_ENABLED} if it finishes successfully. @@ -184,7 +184,7 @@ public class WifiManager { * * @hide */ - public static final int WIFI_AP_STATE_ENABLING = 2; + public static final int WIFI_AP_STATE_ENABLING = 12; /** * Wi-Fi AP is enabled. * @@ -193,7 +193,7 @@ public class WifiManager { * * @hide */ - public static final int WIFI_AP_STATE_ENABLED = 3; + public static final int WIFI_AP_STATE_ENABLED = 13; /** * Wi-Fi AP is in a failed state. This state will occur when an error occurs during * enabling or disabling @@ -203,7 +203,7 @@ public class WifiManager { * * @hide */ - public static final int WIFI_AP_STATE_FAILED = 4; + public static final int WIFI_AP_STATE_FAILED = 14; /** * Broadcast intent action indicating that a connection to the supplicant has diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java index 266d801a3dd4..f2f83438e4ed 100644 --- a/wifi/java/android/net/wifi/WifiMonitor.java +++ b/wifi/java/android/net/wifi/WifiMonitor.java @@ -155,7 +155,7 @@ public class WifiMonitor { public MonitorThread() { super("WifiMonitor"); } - + public void run() { if (connectToSupplicant()) { @@ -272,7 +272,7 @@ public class WifiMonitor { int connectTries = 0; while (true) { - if (mWifiStateTracker.connectToSupplicant()) { + if (WifiNative.connectToSupplicant()) { return true; } if (connectTries++ < 3) { @@ -375,7 +375,7 @@ public class WifiMonitor { if (newSupplicantState == SupplicantState.INVALID) { Log.w(TAG, "Invalid supplicant state: " + newState); } - mWifiStateTracker.notifyStateChange(networkId, BSSID, newSupplicantState); + mWifiStateTracker.notifySupplicantStateChange(networkId, BSSID, newSupplicantState); } } @@ -395,7 +395,7 @@ public class WifiMonitor { } } } - mWifiStateTracker.notifyStateChange(newState, BSSID, networkId); + mWifiStateTracker.notifyNetworkStateChange(newState, BSSID, networkId); } /** diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 7a3282c241d4..47f88138538c 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -41,6 +41,8 @@ public class WifiNative { public native static String getErrorString(int errorCode); public native static boolean loadDriver(); + + public native static boolean isDriverLoaded(); public native static boolean unloadDriver(); diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 5780a04c0c35..96f0354ab57e 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,21 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; +/** + * TODO: Add soft AP states as part of WIFI_STATE_XXX + * Retain WIFI_STATE_ENABLING that indicates driver is loading + * Add WIFI_STATE_AP_ENABLED to indicate soft AP has started + * and WIFI_STATE_FAILED for failure + * Deprecate WIFI_STATE_UNKNOWN + * + * Doing this will simplify the logic for sending broadcasts + */ +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; +import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; + import android.app.ActivityManagerNative; import android.net.NetworkInfo; import android.net.NetworkStateTracker; @@ -31,21 +46,25 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; import android.net.NetworkProperties; +import android.os.Binder; import android.os.Message; import android.os.Parcelable; import android.os.Handler; -import android.os.HandlerThread; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.PowerManager; import android.os.SystemProperties; -import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Process; import android.provider.Settings; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; -import android.util.Config; +import android.util.Slog; import android.app.Notification; import android.app.PendingIntent; +import android.app.backup.IBackupManager; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothA2dp; @@ -54,15 +73,22 @@ import android.content.Intent; import android.content.Context; import android.database.ContentObserver; import com.android.internal.app.IBatteryStats; +import com.android.internal.util.HierarchicalState; +import com.android.internal.util.HierarchicalStateMachine; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.BitSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; /** * Track the state of Wifi connectivity. All event handling is done here, @@ -70,165 +96,61 @@ import java.util.concurrent.atomic.AtomicInteger; * * @hide */ -public class WifiStateTracker extends Handler implements NetworkStateTracker { +//TODO: we still need frequent scanning for the case when +// we issue disconnect but need scan results for open network notification +public class WifiStateTracker extends HierarchicalStateMachine implements NetworkStateTracker { - private static final boolean LOCAL_LOGD = Config.LOGD || false; - private static final String TAG = "WifiStateTracker"; + private static final String NETWORKTYPE = "WIFI"; + private static final boolean DBG = false; - // Event log tags (must be in sync with event-log-tags) - private static final int EVENTLOG_NETWORK_STATE_CHANGED = 50021; - private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50022; - private static final int EVENTLOG_DRIVER_STATE_CHANGED = 50023; - private static final int EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED = 50024; - private static final int EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED = 50025; - - // Event codes - private static final int EVENT_SUPPLICANT_CONNECTION = 1; - private static final int EVENT_SUPPLICANT_DISCONNECT = 2; - private static final int EVENT_SUPPLICANT_STATE_CHANGED = 3; - private static final int EVENT_NETWORK_STATE_CHANGED = 4; - private static final int EVENT_SCAN_RESULTS_AVAILABLE = 5; - private static final int EVENT_INTERFACE_CONFIGURATION_SUCCEEDED = 6; - private static final int EVENT_INTERFACE_CONFIGURATION_FAILED = 7; - private static final int EVENT_POLL_INTERVAL = 8; - private static final int EVENT_DHCP_START = 9; - private static final int EVENT_DEFERRED_DISCONNECT = 10; - private static final int EVENT_DEFERRED_RECONNECT = 11; - /** - * The driver is started or stopped. The object will be the state: true for - * started, false for stopped. - */ - private static final int EVENT_DRIVER_STATE_CHANGED = 12; - private static final int EVENT_PASSWORD_KEY_MAY_BE_INCORRECT = 13; - private static final int EVENT_MAYBE_START_SCAN_POST_DISCONNECT = 14; - - /** - * The driver state indication. - */ - private static final int DRIVER_STARTED = 0; - private static final int DRIVER_STOPPED = 1; - private static final int DRIVER_HUNG = 2; - - /** - * Interval in milliseconds between polling for connection - * status items that are not sent via asynchronous events. - * An example is RSSI (signal strength). - */ - private static final int POLL_STATUS_INTERVAL_MSECS = 3000; - - /** - * The max number of the WPA supplicant loop iterations before we - * decide that the loop should be terminated: - */ - private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4; - - /** - * When a DISCONNECT event is received, we defer handling it to - * allow for the possibility that the DISCONNECT is about to - * be followed shortly by a CONNECT to the same network we were - * just connected to. In such a case, we don't want to report - * the network as down, nor do we want to reconfigure the network - * interface, etc. If we get a CONNECT event for another network - * within the delay window, we immediately handle the pending - * disconnect before processing the CONNECT.<p/> - * The five second delay is chosen somewhat arbitrarily, but is - * meant to cover most of the cases where a DISCONNECT/CONNECT - * happens to a network. - */ - private static final int DISCONNECT_DELAY_MSECS = 5000; - /** - * When the supplicant goes idle after we do an explicit disconnect - * following a DHCP failure, we need to kick the supplicant into - * trying to associate with access points. - */ - private static final int RECONNECT_DELAY_MSECS = 2000; - - /** - * When the supplicant disconnects from an AP it sometimes forgets - * to restart scanning. Wait this delay before asking it to start - * scanning (in case it forgot). 15 sec is the standard delay between - * scans. - */ - private static final int KICKSTART_SCANNING_DELAY_MSECS = 15000; + /* TODO: fetch a configurable interface */ + private static final String SOFTAP_IFACE = "wl0.1"; - /** - * The maximum number of times we will retry a connection to an access point - * for which we have failed in acquiring an IP address from DHCP. A value of - * N means that we will make N+1 connection attempts in all. - * <p> - * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default - * value if a Settings value is not present. - */ - private static final int DEFAULT_MAX_DHCP_RETRIES = 9; - - private static final int DRIVER_POWER_MODE_AUTO = 0; - private static final int DRIVER_POWER_MODE_ACTIVE = 1; + private WifiMonitor mWifiMonitor; + private INetworkManagementService nwService; + private ConnectivityManager mCm; - /** - * The current WPA supplicant loop state (used to detect looping behavior): - */ - private SupplicantState mSupplicantLoopState = SupplicantState.DISCONNECTED; + /* Scan results handling */ + private List<ScanResult> mScanResults; + private static final Pattern scanResultPattern = Pattern.compile("\t+"); + private static final int SCAN_RESULT_CACHE_SIZE = 80; + private final LinkedHashMap<String, ScanResult> mScanResultCache; - /** - * The current number of WPA supplicant loop iterations: - */ - private int mNumSupplicantLoopIterations = 0; + private String mInterfaceName; + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); + private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); + private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); + private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); - /** - * The current number of supplicant state changes. This is used to determine - * if we've received any new info since we found out it was DISCONNECTED or - * INACTIVE. If we haven't for X ms, we then request a scan - it should have - * done that automatically, but sometimes some firmware does not. - */ - private int mNumSupplicantStateChanges = 0; + private int mNumAllowedChannels = 0; + private int mLastSignalLevel = -1; + private String mLastBssid; + private int mLastNetworkId; + private boolean mEnableRssiPolling = false; + private boolean mPasswordKeyMayBeIncorrect = false; + private boolean mUseStaticIp = false; + private int mReconnectCount = 0; + private boolean mIsScanMode = false; /** - * True if we received an event that that a password-key may be incorrect. - * If the next incoming supplicant state change event is DISCONNECT, - * broadcast a message that we have a possible password error and disable - * the network. + * Instance of the bluetooth headset helper. This needs to be created + * early because there is a delay before it actually 'connects', as + * noted by its javadoc. If we check before it is connected, it will be + * in an error state and we will not disable coexistence. */ - private boolean mPasswordKeyMayBeIncorrect = false; + private BluetoothHeadset mBluetoothHeadset; - public static final int SUPPL_SCAN_HANDLING_NORMAL = 1; - public static final int SUPPL_SCAN_HANDLING_LIST_ONLY = 2; + private BluetoothA2dp mBluetoothA2dp; - private WifiMonitor mWifiMonitor; - private WifiInfo mWifiInfo; - private List<ScanResult> mScanResults; - private WifiManager mWM; - private boolean mHaveIpAddress; - private boolean mObtainingIpAddress; - private boolean mTornDownByConnMgr; - private NetworkInfo mNetworkInfo; - private boolean mTeardownRequested = false; - /** - * A DISCONNECT event has been received, but processing it - * is being deferred. - */ - private boolean mDisconnectPending; /** - * An operation has been performed as a result of which we expect the next event - * will be a DISCONNECT. + * Observes the static IP address settings. */ - private boolean mDisconnectExpected; - private DhcpHandler mDhcpTarget; - private DhcpInfo mDhcpInfo; + private SettingsObserver mSettingsObserver; private NetworkProperties mNetworkProperties; - private int mLastSignalLevel = -1; - private String mLastBssid; - private String mLastSsid; - private int mLastNetworkId = -1; - private boolean mUseStaticIp = false; - private int mReconnectCount; - // used to store the (non-persisted) num determined during device boot - // (from mcc or other phone info) before the driver is started. - private int mNumAllowedChannels = 0; // Variables relating to the 'available networks' notification - /** * The icon to show in the 'available networks' notification. This will also * be the ID of the Notification given to the NotificationManager. @@ -276,13 +198,230 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * something other than scanning, we reset this to 0. */ private int mNumScansSinceNetworkStateChange; + + // Held during driver load and unload + private static PowerManager.WakeLock sWakeLock; + + /* For sending events to connectivity service handler */ + private Handler mCsHandler; + private Context mContext; + + private DhcpInfo mDhcpInfo; + private WifiInfo mWifiInfo; + private NetworkInfo mNetworkInfo; + private SupplicantStateTracker mSupplicantStateTracker; + + // Event log tags (must be in sync with event-log-tags) + private static final int EVENTLOG_WIFI_STATE_CHANGED = 50021; + private static final int EVENTLOG_WIFI_EVENT_HANDLED = 50022; + private static final int EVENTLOG_SUPPLICANT_STATE_CHANGED = 50023; + + /* Load the driver */ + private static final int CMD_LOAD_DRIVER = 1; + /* Unload the driver */ + private static final int CMD_UNLOAD_DRIVER = 2; + /* Indicates driver load succeeded */ + private static final int CMD_LOAD_DRIVER_SUCCESS = 3; + /* Indicates driver load failed */ + private static final int CMD_LOAD_DRIVER_FAILURE = 4; + /* Indicates driver unload succeeded */ + private static final int CMD_UNLOAD_DRIVER_SUCCESS = 5; + /* Indicates driver unload failed */ + private static final int CMD_UNLOAD_DRIVER_FAILURE = 6; + + /* Start the supplicant */ + private static final int CMD_START_SUPPLICANT = 11; + /* Stop the supplicant */ + private static final int CMD_STOP_SUPPLICANT = 12; + /* Start the driver */ + private static final int CMD_START_DRIVER = 13; + /* Start the driver */ + private static final int CMD_STOP_DRIVER = 14; + /* Indicates DHCP succeded */ + private static final int CMD_IP_CONFIG_SUCCESS = 15; + /* Indicates DHCP failed */ + private static final int CMD_IP_CONFIG_FAILURE = 16; + /* Re-configure interface */ + private static final int CMD_RECONFIGURE_IP = 17; + + + /* Start the soft access point */ + private static final int CMD_START_AP = 21; + /* Stop the soft access point */ + private static final int CMD_STOP_AP = 22; + + + /* Supplicant events */ + /* Connection to supplicant established */ + private static final int SUP_CONNECTION_EVENT = 31; + /* Connection to supplicant lost */ + private static final int SUP_DISCONNECTION_EVENT = 32; + /* Driver start completed */ + private static final int DRIVER_START_EVENT = 33; + /* Driver stop completed */ + private static final int DRIVER_STOP_EVENT = 34; + /* Network connection completed */ + private static final int NETWORK_CONNECTION_EVENT = 36; + /* Network disconnection completed */ + private static final int NETWORK_DISCONNECTION_EVENT = 37; + /* Scan results are available */ + private static final int SCAN_RESULTS_EVENT = 38; + /* Supplicate state changed */ + private static final int SUPPLICANT_STATE_CHANGE_EVENT = 39; + /* Password may be incorrect */ + private static final int PASSWORD_MAY_BE_INCORRECT_EVENT = 40; + + /* Supplicant commands */ + /* Is supplicant alive ? */ + private static final int CMD_PING_SUPPLICANT = 51; + /* Add/update a network configuration */ + private static final int CMD_ADD_OR_UPDATE_NETWORK = 52; + /* Delete a network */ + private static final int CMD_REMOVE_NETWORK = 53; + /* Enable a network. The device will attempt a connection to the given network. */ + private static final int CMD_ENABLE_NETWORK = 54; + /* Disable a network. The device does not attempt a connection to the given network. */ + private static final int CMD_DISABLE_NETWORK = 55; + /* Blacklist network. De-prioritizes the given BSSID for connection. */ + private static final int CMD_BLACKLIST_NETWORK = 56; + /* Clear the blacklist network list */ + private static final int CMD_CLEAR_BLACKLIST = 57; + /* Get the configured networks */ + private static final int CMD_GET_NETWORK_CONFIG = 58; + /* Save configuration */ + private static final int CMD_SAVE_CONFIG = 59; + /* Connection status */ + private static final int CMD_CONNECTION_STATUS = 60; + + /* Supplicant commands after driver start*/ + /* Initiate a scan */ + private static final int CMD_START_SCAN = 71; + /* Set scan mode. CONNECT_MODE or SCAN_ONLY_MODE */ + private static final int CMD_SET_SCAN_MODE = 72; + /* Set scan type. SCAN_ACTIVE or SCAN_PASSIVE */ + private static final int CMD_SET_SCAN_TYPE = 73; + /* Disconnect from a network */ + private static final int CMD_DISCONNECT = 74; + /* Reconnect to a network */ + private static final int CMD_RECONNECT = 75; + /* Reassociate to a network */ + private static final int CMD_REASSOCIATE = 76; + /* Set power mode + * POWER_MODE_ACTIVE + * POWER_MODE_AUTO + */ + private static final int CMD_SET_POWER_MODE = 77; + /* Set bluetooth co-existence + * BLUETOOTH_COEXISTENCE_MODE_ENABLED + * BLUETOOTH_COEXISTENCE_MODE_DISABLED + * BLUETOOTH_COEXISTENCE_MODE_SENSE + */ + private static final int CMD_SET_BLUETOOTH_COEXISTENCE = 78; + /* Enable/disable bluetooth scan mode + * true(1) + * false(0) + */ + private static final int CMD_SET_BLUETOOTH_SCAN_MODE = 79; + /* Set number of allowed channels */ + private static final int CMD_SET_NUM_ALLOWED_CHANNELS = 80; + /* Request connectivity manager wake lock before driver stop */ + private static final int CMD_REQUEST_CM_WAKELOCK = 81; + /* Enables RSSI poll */ + private static final int CMD_ENABLE_RSSI_POLL = 82; + /* RSSI poll */ + private static final int CMD_RSSI_POLL = 83; + /* Get current RSSI */ + private static final int CMD_GET_RSSI = 84; + /* Get approx current RSSI */ + private static final int CMD_GET_RSSI_APPROX = 85; + /* Get link speed on connection */ + private static final int CMD_GET_LINK_SPEED = 86; + /* Radio mac address */ + private static final int CMD_GET_MAC_ADDR = 87; + /* Set up packet filtering */ + private static final int CMD_START_PACKET_FILTERING = 88; + /* Clear packet filter */ + private static final int CMD_STOP_PACKET_FILTERING = 89; + + /* Connectivity service commands */ + /* Bring down wifi connection */ + private static final int CM_CMD_TEARDOWN = 110; + /* Reconnect to wifi */ + private static final int CM_CMD_RECONNECT = 111; + /** - * Observes the static IP address settings. + * Interval in milliseconds between polling for connection + * status items that are not sent via asynchronous events. + * An example is RSSI (signal strength). */ - private SettingsObserver mSettingsObserver; - - private boolean mIsScanModeActive; - private boolean mEnableRssiPolling; + private static final int POLL_RSSI_INTERVAL_MSECS = 3000; + + private static final int CONNECT_MODE = 1; + private static final int SCAN_ONLY_MODE = 2; + + private static final int SCAN_ACTIVE = 1; + private static final int SCAN_PASSIVE = 2; + + /** + * The maximum number of times we will retry a connection to an access point + * for which we have failed in acquiring an IP address from DHCP. A value of + * N means that we will make N+1 connection attempts in all. + * <p> + * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default + * value if a Settings value is not present. + */ + private static final int DEFAULT_MAX_DHCP_RETRIES = 9; + + private static final int DRIVER_POWER_MODE_ACTIVE = 1; + private static final int DRIVER_POWER_MODE_AUTO = 0; + + /* Default parent state */ + private HierarchicalState mDefaultState = new DefaultState(); + /* Temporary initial state */ + private HierarchicalState mInitialState = new InitialState(); + /* Unloading the driver */ + private HierarchicalState mDriverUnloadingState = new DriverUnloadingState(); + /* Loading the driver */ + private HierarchicalState mDriverUnloadedState = new DriverUnloadedState(); + /* Driver load/unload failed */ + private HierarchicalState mDriverFailedState = new DriverFailedState(); + /* Driver loading */ + private HierarchicalState mDriverLoadingState = new DriverLoadingState(); + /* Driver loaded */ + private HierarchicalState mDriverLoadedState = new DriverLoadedState(); + /* Driver loaded, waiting for supplicant to start */ + private HierarchicalState mWaitForSupState = new WaitForSupState(); + + /* Driver loaded and supplicant ready */ + private HierarchicalState mDriverSupReadyState = new DriverSupReadyState(); + /* Driver start issued, waiting for completed event */ + private HierarchicalState mDriverStartingState = new DriverStartingState(); + /* Driver started */ + private HierarchicalState mDriverStartedState = new DriverStartedState(); + /* Driver stopping */ + private HierarchicalState mDriverStoppingState = new DriverStoppingState(); + /* Driver stopped */ + private HierarchicalState mDriverStoppedState = new DriverStoppedState(); + /* Scan for networks, no connection will be established */ + private HierarchicalState mScanModeState = new ScanModeState(); + /* Connecting to an access point */ + private HierarchicalState mConnectModeState = new ConnectModeState(); + /* Fetching IP after network connection (assoc+auth complete) */ + private HierarchicalState mConnectingState = new ConnectingState(); + /* Connected with IP addr */ + private HierarchicalState mConnectedState = new ConnectedState(); + /* disconnect issued, waiting for network disconnect confirmation */ + private HierarchicalState mDisconnectingState = new DisconnectingState(); + /* Network is not connected, supplicant assoc+auth is not complete */ + private HierarchicalState mDisconnectedState = new DisconnectedState(); + + /* Soft Ap is running */ + private HierarchicalState mSoftApStartedState = new SoftApStartedState(); + + /* Argument for Message object to indicate a synchronous call */ + private static final int SYNCHRONOUS_CALL = 1; + private static final int ASYNCHRONOUS_CALL = 0; + /** * One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -291,242 +430,153 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * {@link WifiManager#WIFI_STATE_ENABLING}, * {@link WifiManager#WIFI_STATE_UNKNOWN} * - * getWifiState() is not synchronized to make sure it's always fast, - * even when the instance lock is held on other slow operations. - * Use a atomic variable for state. - */ - private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_UNKNOWN); - - // Wi-Fi run states: - private static final int RUN_STATE_STARTING = 1; - private static final int RUN_STATE_RUNNING = 2; - private static final int RUN_STATE_STOPPING = 3; - private static final int RUN_STATE_STOPPED = 4; - - private static final String mRunStateNames[] = { - "Starting", - "Running", - "Stopping", - "Stopped" - }; - private int mRunState; + */ + private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED); - private final IBatteryStats mBatteryStats; + /** + * One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, + * {@link WifiManager#WIFI_AP_STATE_DISABLING}, + * {@link WifiManager#WIFI_AP_STATE_ENABLED}, + * {@link WifiManager#WIFI_AP_STATE_ENABLING}, + * {@link WifiManager#WIFI_AP_STATE_FAILED} + * + */ + private final AtomicInteger mWifiApState = new AtomicInteger(WIFI_AP_STATE_DISABLED); - private boolean mIsScanOnly; + private final AtomicInteger mLastEnableUid = new AtomicInteger(Process.myUid()); + private final AtomicInteger mLastApEnableUid = new AtomicInteger(Process.myUid()); - private BluetoothA2dp mBluetoothA2dp; + private final IBatteryStats mBatteryStats; - private String mInterfaceName; - private static String LS = System.getProperty("line.separator"); + public WifiStateTracker(Context context, Handler target) { + super(NETWORKTYPE); - private Handler mTarget; - private Context mContext; - private boolean mPrivateDnsRouteSet = false; - private int mDefaultGatewayAddr = 0; - private boolean mDefaultRouteSet = false; + mCsHandler = target; + mContext = context; - /** - * A structure for supplying information about a supplicant state - * change in the STATE_CHANGE event message that comes from the - * WifiMonitor - * thread. - */ - private static class SupplicantStateChangeResult { - SupplicantStateChangeResult(int networkId, String BSSID, SupplicantState state) { - this.state = state; - this.BSSID = BSSID; - this.networkId = networkId; - } - int networkId; - String BSSID; - SupplicantState state; - } + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, ""); + mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); - /** - * A structure for supplying information about a connection in - * the CONNECTED event message that comes from the WifiMonitor - * thread. - */ - private static class NetworkStateChangeResult { - NetworkStateChangeResult(DetailedState state, String BSSID, int networkId) { - this.state = state; - this.BSSID = BSSID; - this.networkId = networkId; - } - DetailedState state; - String BSSID; - int networkId; - } + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + nwService = INetworkManagementService.Stub.asInterface(b); - public WifiStateTracker(Context context, Handler target) { - mTarget = target; - mContext = context; - mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); - mWifiInfo = new WifiInfo(); mWifiMonitor = new WifiMonitor(this); - mHaveIpAddress = false; - mObtainingIpAddress = false; - setTornDownByConnMgr(false); - mDisconnectPending = false; - mScanResults = new ArrayList<ScanResult>(); - // Allocate DHCP info object once, and fill it in on each request mDhcpInfo = new DhcpInfo(); - mRunState = RUN_STATE_STARTING; + mWifiInfo = new WifiInfo(); + mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); + mSupplicantStateTracker = new SupplicantStateTracker(context, getHandler()); + + mBluetoothHeadset = new BluetoothHeadset(mContext, null); + mNetworkProperties = new NetworkProperties(); + + mNetworkInfo.setIsAvailable(false); + mNetworkProperties.clear(); + resetNotificationTimer(); + setTeardownRequested(false); + mLastBssid = null; + mLastNetworkId = -1; + mLastSignalLevel = -1; + + mScanResultCache = new LinkedHashMap<String, ScanResult>( + SCAN_RESULT_CACHE_SIZE, 0.75f, true) { + /* + * Limit the cache size by SCAN_RESULT_CACHE_SIZE + * elements + */ + @Override + public boolean removeEldestEntry(Map.Entry eldest) { + return SCAN_RESULT_CACHE_SIZE < this.size(); + } + }; // Setting is in seconds - NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), + NOTIFICATION_REPEAT_DELAY_MS = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; - mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); + mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(target); mNotificationEnabledSettingObserver.register(); mSettingsObserver = new SettingsObserver(new Handler()); - mInterfaceName = SystemProperties.get("wifi.interface", "tiwlan0"); - mNetworkProperties = new NetworkProperties(); - mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); - - } + PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + sWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - /** - * Helper method: sets the supplicant state and keeps the network - * info updated. - * @param state the new state - */ - private void setSupplicantState(SupplicantState state) { - mWifiInfo.setSupplicantState(state); - updateNetworkInfo(); - checkPollTimer(); - } + addState(mDefaultState); + addState(mInitialState, mDefaultState); + addState(mDriverUnloadingState, mDefaultState); + addState(mDriverUnloadedState, mDefaultState); + addState(mDriverFailedState, mDriverUnloadedState); + addState(mDriverLoadingState, mDefaultState); + addState(mDriverLoadedState, mDefaultState); + addState(mWaitForSupState, mDriverLoadedState); + addState(mDriverSupReadyState, mDefaultState); + addState(mDriverStartingState, mDriverSupReadyState); + addState(mDriverStartedState, mDriverSupReadyState); + addState(mScanModeState, mDriverStartedState); + addState(mConnectModeState, mDriverStartedState); + addState(mConnectingState, mConnectModeState); + addState(mConnectedState, mConnectModeState); + addState(mDisconnectingState, mConnectModeState); + addState(mDisconnectedState, mConnectModeState); + addState(mDriverStoppingState, mDriverSupReadyState); + addState(mDriverStoppedState, mDriverSupReadyState); + addState(mSoftApStartedState, mDefaultState); - public SupplicantState getSupplicantState() { - return mWifiInfo.getSupplicantState(); - } + setInitialState(mInitialState); - /** - * Helper method: sets the supplicant state and keeps the network - * info updated (string version). - * @param stateName the string name of the new state - */ - private void setSupplicantState(String stateName) { - mWifiInfo.setSupplicantState(stateName); - updateNetworkInfo(); - checkPollTimer(); - } + if (DBG) setDbg(true); - /** - * Record the detailed state of a network, and if it is a - * change from the previous state, send a notification to - * any listeners. - * @param state the new @{code DetailedState} - */ - private void setDetailedState(NetworkInfo.DetailedState state) { - setDetailedState(state, null, null); + //start the state machine + start(); } - /** - * Record the detailed state of a network, and if it is a - * change from the previous state, send a notification to - * any listeners. - * @param state the new @{code DetailedState} - * @param reason a {@code String} indicating a reason for the state change, - * if one was supplied. May be {@code null}. - * @param extraInfo optional {@code String} providing extra information about the state change - */ - private void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) { - if (LOCAL_LOGD) Log.d(TAG, "setDetailed state, old =" - + mNetworkInfo.getDetailedState() + " and new state=" + state); - if (state != mNetworkInfo.getDetailedState()) { - boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING); - String lastReason = mNetworkInfo.getReason(); - /* - * If a reason was supplied when the CONNECTING state was entered, and no - * reason was supplied for entering the CONNECTED state, then retain the - * reason that was supplied when going to CONNECTING. - */ - if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null - && lastReason != null) - reason = lastReason; - mNetworkInfo.setDetailedState(state, reason, extraInfo); - Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); - msg.sendToTarget(); - } - } - private void setDetailedStateInternal(NetworkInfo.DetailedState state) { - mNetworkInfo.setDetailedState(state, null, null); - } + /********************************************************* + * NetworkStateTracker interface implementation + ********************************************************/ public void setTeardownRequested(boolean isRequested) { - mTeardownRequested = isRequested; + mTeardownRequested.set(isRequested); } public boolean isTeardownRequested() { - return mTeardownRequested; + return mTeardownRequested.get(); } /** - * Helper method: sets the boolean indicating that the connection - * manager asked the network to be torn down (and so only the connection - * manager can set it up again). - * network info updated. - * @param flag {@code true} if explicitly disabled. + * Begin monitoring wifi connectivity + * @deprecated + * + * TODO: remove this from callers */ - private void setTornDownByConnMgr(boolean flag) { - mTornDownByConnMgr = flag; - updateNetworkInfo(); + public void startMonitoring() { } /** - * Return the name of our WLAN network interface. - * @return the name of our interface. + * Disable connectivity to a network + * TODO: do away with return value after making MobileDataStateTracker async */ - public String getInterfaceName() { - return mInterfaceName; - } - - public boolean isPrivateDnsRouteSet() { - return mPrivateDnsRouteSet; - } - - public void privateDnsRouteSet(boolean enabled) { - mPrivateDnsRouteSet = enabled; - } - - public NetworkInfo getNetworkInfo() { - return mNetworkInfo; - } - - public int getDefaultGatewayAddr() { - return mDefaultGatewayAddr; - } - - public boolean isDefaultRouteSet() { - return mDefaultRouteSet; - } - - public void defaultRouteSet(boolean enabled) { - mDefaultRouteSet = enabled; + public boolean teardown() { + sendMessage(CM_CMD_TEARDOWN); + return true; } /** - * Return the system properties name associated with the tcp buffer sizes - * for this network. + * Re-enable connectivity to a network after a {@link #teardown()}. + * TODO: do away with return value after making MobileDataStateTracker async */ - public String getTcpBufferSizesPropName() { - return "net.tcp.buffersize.wifi"; - } - - public void startMonitoring() { - /* - * Get a handle on the WifiManager. This cannot be done in our - * constructor, because the Wifi service is not yet registered. - */ - mWM = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); + public boolean reconnect() { + sendMessage(CM_CMD_RECONNECT); + return true; } - public void startEventLoop() { - mWifiMonitor.startMonitoring(); + /** + * Turn the wireless radio off for a network. + * @param turnOn {@code true} to turn the radio on, {@code false} + * TODO: do away with return value after making MobileDataStateTracker async + */ + public boolean setRadio(boolean turnOn) { + setWifiEnabled(turnOn); + return true; } /** @@ -538,1279 +588,334 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * unavailable. * @return {@code true} if Wi-Fi connections are possible */ - public synchronized boolean isAvailable() { - /* - * TODO: Need to also look at scan results to see whether we're - * in range of any access points. If we have scan results that - * are no more than N seconds old, use those, otherwise, initiate - * a scan and wait for the results. This only matters if we - * allow mobile to be the preferred network. - */ - SupplicantState suppState = mWifiInfo.getSupplicantState(); - return suppState != SupplicantState.UNINITIALIZED && - suppState != SupplicantState.INACTIVE && - (mTornDownByConnMgr || !isDriverStopped()); - } - - /** - * {@inheritDoc} - * There are currently no defined Wi-Fi subtypes. - */ - public int getNetworkSubtype() { - return 0; - } - - /** - * Helper method: updates the network info object to keep it in sync with - * the Wi-Fi state tracker. - */ - private void updateNetworkInfo() { - mNetworkInfo.setIsAvailable(isAvailable()); - } - - /** - * Report whether the Wi-Fi connection is fully configured for data. - * @return {@code true} if the {@link SupplicantState} is - * {@link android.net.wifi.SupplicantState#COMPLETED COMPLETED}. - */ - public boolean isConnectionCompleted() { - return mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED; - } - - /** - * Report whether the Wi-Fi connection has successfully acquired an IP address. - * @return {@code true} if the Wi-Fi connection has been assigned an IP address. - */ - public boolean hasIpAddress() { - return mHaveIpAddress; - } - - /** - * Send the tracker a notification that a user-entered password key - * may be incorrect (i.e., caused authentication to fail). - */ - void notifyPasswordKeyMayBeIncorrect() { - sendEmptyMessage(EVENT_PASSWORD_KEY_MAY_BE_INCORRECT); - } - - /** - * Send the tracker a notification that a connection to the supplicant - * daemon has been established. - */ - void notifySupplicantConnection() { - sendEmptyMessage(EVENT_SUPPLICANT_CONNECTION); - } - - /** - * Send the tracker a notification that the state of the supplicant - * has changed. - * @param networkId the configured network on which the state change occurred - * @param newState the new {@code SupplicantState} - */ - void notifyStateChange(int networkId, String BSSID, SupplicantState newState) { - Message msg = Message.obtain( - this, EVENT_SUPPLICANT_STATE_CHANGED, - new SupplicantStateChangeResult(networkId, BSSID, newState)); - msg.sendToTarget(); - } - - /** - * Send the tracker a notification that the state of Wifi connectivity - * has changed. - * @param networkId the configured network on which the state change occurred - * @param newState the new network state - * @param BSSID when the new state is {@link DetailedState#CONNECTED - * NetworkInfo.DetailedState.CONNECTED}, - * this is the MAC address of the access point. Otherwise, it - * is {@code null}. - */ - void notifyStateChange(DetailedState newState, String BSSID, int networkId) { - Message msg = Message.obtain( - this, EVENT_NETWORK_STATE_CHANGED, - new NetworkStateChangeResult(newState, BSSID, networkId)); - msg.sendToTarget(); + public boolean isAvailable() { + return mNetworkInfo.isAvailable(); } /** - * Send the tracker a notification that a scan has completed, and results - * are available. + * Tells the underlying networking system that the caller wants to + * begin using the named feature. The interpretation of {@code feature} + * is completely up to each networking implementation. + * @param feature the name of the feature to be used + * @param callingPid the process ID of the process that is issuing this request + * @param callingUid the user ID of the process that is issuing this request + * @return an integer value representing the outcome of the request. + * The interpretation of this value is specific to each networking + * implementation+feature combination, except that the value {@code -1} + * always indicates failure. + * TODO: needs to go away */ - void notifyScanResultsAvailable() { - // reset the supplicant's handling of scan results to "normal" mode - setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL); - sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE); + public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { + return -1; } /** - * Send the tracker a notification that we can no longer communicate with - * the supplicant daemon. + * Tells the underlying networking system that the caller is finished + * using the named feature. The interpretation of {@code feature} + * is completely up to each networking implementation. + * @param feature the name of the feature that is no longer needed. + * @param callingPid the process ID of the process that is issuing this request + * @param callingUid the user ID of the process that is issuing this request + * @return an integer value representing the outcome of the request. + * The interpretation of this value is specific to each networking + * implementation+feature combination, except that the value {@code -1} + * always indicates failure. + * TODO: needs to go away */ - void notifySupplicantLost() { - sendEmptyMessage(EVENT_SUPPLICANT_DISCONNECT); + public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { + return -1; } - /** - * Send the tracker a notification that the Wi-Fi driver has been stopped. + /* TODO: will go away. + * Notifications are directly handled in WifiStateTracker at checkAndSetNotification() */ - void notifyDriverStopped() { - mRunState = RUN_STATE_STOPPED; + public void interpretScanResultsAvailable() { - // Send a driver stopped message to our handler - Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STOPPED, 0).sendToTarget(); } /** - * Send the tracker a notification that the Wi-Fi driver has been restarted after - * having been stopped. - */ - void notifyDriverStarted() { - // Send a driver started message to our handler - Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_STARTED, 0).sendToTarget(); - } - - /** - * Send the tracker a notification that the Wi-Fi driver has hung and needs restarting. + * Return the name of our WLAN network interface. + * @return the name of our interface. */ - void notifyDriverHung() { - // Send a driver hanged message to our handler - Message.obtain(this, EVENT_DRIVER_STATE_CHANGED, DRIVER_HUNG, 0).sendToTarget(); + public String getInterfaceName() { + return mInterfaceName; } /** - * Set the interval timer for polling connection information - * that is not delivered asynchronously. + * Check if private DNS route is set for the network */ - private synchronized void checkPollTimer() { - if (mEnableRssiPolling && - mWifiInfo.getSupplicantState() == SupplicantState.COMPLETED && - !hasMessages(EVENT_POLL_INTERVAL)) { - sendEmptyMessageDelayed(EVENT_POLL_INTERVAL, POLL_STATUS_INTERVAL_MSECS); - } + public boolean isPrivateDnsRouteSet() { + return mPrivateDnsRouteSet.get(); } /** - * TODO: mRunState is not synchronized in some places - * address this as part of re-architect. - * - * TODO: We are exposing an additional public synchronized call - * for a wakelock optimization in WifiService. Remove it - * when we handle the wakelock in ConnectivityService. + * Set a flag indicating private DNS route is set */ - public synchronized boolean isDriverStopped() { - return mRunState == RUN_STATE_STOPPED || mRunState == RUN_STATE_STOPPING; - } - - private void noteRunState() { - try { - if (mRunState == RUN_STATE_RUNNING) { - mBatteryStats.noteWifiRunning(); - } else if (mRunState == RUN_STATE_STOPPED) { - mBatteryStats.noteWifiStopped(); - } - } catch (RemoteException ignore) { - } + public void privateDnsRouteSet(boolean enabled) { + mPrivateDnsRouteSet.set(enabled); } /** - * Set the run state to either "normal" or "scan-only". - * @param scanOnlyMode true if the new mode should be scan-only. + * Fetch NetworkInfo for the network */ - public synchronized void setScanOnlyMode(boolean scanOnlyMode) { - // do nothing unless scan-only mode is changing - if (mIsScanOnly != scanOnlyMode) { - int scanType = (scanOnlyMode ? - SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL); - if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType); - if (setScanResultHandling(scanType)) { - mIsScanOnly = scanOnlyMode; - if (!isDriverStopped()) { - if (scanOnlyMode) { - disconnect(); - } else { - reconnectCommand(); - } - } - } - } - } - - - private void checkIsBluetoothPlaying() { - boolean isBluetoothPlaying = false; - Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks(); - - for (BluetoothDevice device : connected) { - if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) { - isBluetoothPlaying = true; - break; - } - } - setBluetoothScanMode(isBluetoothPlaying); - } - - public void enableRssiPolling(boolean enable) { - if (mEnableRssiPolling != enable) { - mEnableRssiPolling = enable; - checkPollTimer(); - } + public NetworkInfo getNetworkInfo() { + return mNetworkInfo; } /** - * Tracks the WPA supplicant states to detect "loop" situations. - * @param newSupplicantState The new WPA supplicant state. - * @return {@code true} if the supplicant loop should be stopped - * and {@code false} if it should continue. + * Fetch NetworkProperties for the network */ - private boolean isSupplicantLooping(SupplicantState newSupplicantState) { - if (SupplicantState.ASSOCIATING.ordinal() <= newSupplicantState.ordinal() - && newSupplicantState.ordinal() < SupplicantState.COMPLETED.ordinal()) { - if (mSupplicantLoopState != newSupplicantState) { - if (newSupplicantState.ordinal() < mSupplicantLoopState.ordinal()) { - ++mNumSupplicantLoopIterations; - } - - mSupplicantLoopState = newSupplicantState; - } - } else if (newSupplicantState == SupplicantState.COMPLETED) { - resetSupplicantLoopState(); - } - - return mNumSupplicantLoopIterations >= MAX_SUPPLICANT_LOOP_ITERATIONS; + public NetworkProperties getNetworkProperties() { + return mNetworkProperties; } /** - * Resets the WPA supplicant loop state. + * Fetch default gateway address for the network */ - private void resetSupplicantLoopState() { - mNumSupplicantLoopIterations = 0; - } - - @Override - public void handleMessage(Message msg) { - Intent intent; - - switch (msg.what) { - case EVENT_SUPPLICANT_CONNECTION: - mRunState = RUN_STATE_RUNNING; - noteRunState(); - checkUseStaticIp(); - /* Reset notification state on new connection */ - resetNotificationTimer(); - /* - * DHCP requests are blocking, so run them in a separate thread. - */ - HandlerThread dhcpThread = new HandlerThread("DHCP Handler Thread"); - dhcpThread.start(); - mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this); - mIsScanModeActive = true; - mTornDownByConnMgr = false; - mLastBssid = null; - mLastSsid = null; - requestConnectionInfo(); - SupplicantState supplState = mWifiInfo.getSupplicantState(); - /** - * The MAC address isn't going to change, so just request it - * once here. - */ - String macaddr = getMacAddress(); - - if (macaddr != null) { - mWifiInfo.setMacAddress(macaddr); - } - if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant established, state=" + - supplState); - // Wi-Fi supplicant connection state changed: - // [31- 2] Reserved for future use - // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) , - // or supplicant died (2) - EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, 1); - /* - * The COMPLETED state change from the supplicant may have occurred - * in between polling for supplicant availability, in which case - * we didn't perform a DHCP request to get an IP address. - */ - if (supplState == SupplicantState.COMPLETED) { - mLastBssid = mWifiInfo.getBSSID(); - mLastSsid = mWifiInfo.getSSID(); - configureInterface(); - } - if (ActivityManagerNative.isSystemReady()) { - intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); - intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, true); - mContext.sendBroadcast(intent); - } - if (supplState == SupplicantState.COMPLETED && mHaveIpAddress) { - setDetailedState(DetailedState.CONNECTED); - } else { - setDetailedState(WifiInfo.getDetailedStateOf(supplState)); - } - /* - * Filter out multicast packets. This saves battery power, since - * the CPU doesn't have to spend time processing packets that - * are going to end up being thrown away. - */ - mWM.initializeMulticastFiltering(); - - if (mBluetoothA2dp == null) { - mBluetoothA2dp = new BluetoothA2dp(mContext); - } - checkIsBluetoothPlaying(); - - // initialize this after the supplicant is alive - setNumAllowedChannels(); - break; - - case EVENT_SUPPLICANT_DISCONNECT: - mRunState = RUN_STATE_STOPPED; - noteRunState(); - boolean died = mWifiState.get() != WIFI_STATE_DISABLED && - mWifiState.get() != WIFI_STATE_DISABLING; - if (died) { - if (LOCAL_LOGD) Log.v(TAG, "Supplicant died unexpectedly"); - } else { - if (LOCAL_LOGD) Log.v(TAG, "Connection to supplicant lost"); - } - // Wi-Fi supplicant connection state changed: - // [31- 2] Reserved for future use - // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) , - // or supplicant died (2) - EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0); - closeSupplicantConnection(); - - if (died) { - resetConnections(true); - } - // When supplicant dies, kill the DHCP thread - if (mDhcpTarget != null) { - mDhcpTarget.getLooper().quit(); - mDhcpTarget = null; - } - mContext.removeStickyBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION)); - if (ActivityManagerNative.isSystemReady()) { - intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); - intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false); - mContext.sendBroadcast(intent); - } - setDetailedState(DetailedState.DISCONNECTED); - setSupplicantState(SupplicantState.UNINITIALIZED); - mNetworkProperties.clear(); - mHaveIpAddress = false; - mObtainingIpAddress = false; - if (died) { - mWM.setWifiEnabled(false); - } - break; - - case EVENT_MAYBE_START_SCAN_POST_DISCONNECT: - // Only do this if we haven't gotten a new supplicant status since the timer - // started - if (mNumSupplicantStateChanges == msg.arg1) { - scan(false); // do a passive scan - } - break; - - case EVENT_SUPPLICANT_STATE_CHANGED: - mNumSupplicantStateChanges++; - SupplicantStateChangeResult supplicantStateResult = - (SupplicantStateChangeResult) msg.obj; - SupplicantState newState = supplicantStateResult.state; - SupplicantState currentState = mWifiInfo.getSupplicantState(); - - // Wi-Fi supplicant state changed: - // [31- 6] Reserved for future use - // [ 5- 0] Supplicant state ordinal (as defined by SupplicantState) - int eventLogParam = (newState.ordinal() & 0x3f); - EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, eventLogParam); - - if (LOCAL_LOGD) Log.v(TAG, "Changing supplicant state: " - + currentState + - " ==> " + newState); - - int networkId = supplicantStateResult.networkId; - - /** - * The SupplicantState BSSID value is valid in ASSOCIATING state only. - * The NetworkState BSSID value comes upon a successful connection. - */ - if (supplicantStateResult.state == SupplicantState.ASSOCIATING) { - mLastBssid = supplicantStateResult.BSSID; - } - /* - * If we get disconnect or inactive we need to start our - * watchdog timer to start a scan - */ - if (newState == SupplicantState.DISCONNECTED || - newState == SupplicantState.INACTIVE) { - sendMessageDelayed(obtainMessage(EVENT_MAYBE_START_SCAN_POST_DISCONNECT, - mNumSupplicantStateChanges, 0), KICKSTART_SCANNING_DELAY_MSECS); - } - - - /* - * Did we get to DISCONNECTED state due to an - * authentication (password) failure? - */ - boolean failedToAuthenticate = false; - if (newState == SupplicantState.DISCONNECTED) { - failedToAuthenticate = mPasswordKeyMayBeIncorrect; - } - mPasswordKeyMayBeIncorrect = false; - - /* - * Keep track of the supplicant state and check if we should - * disable the network - */ - boolean disabledNetwork = false; - if (isSupplicantLooping(newState)) { - if (LOCAL_LOGD) { - Log.v(TAG, - "Stop WPA supplicant loop and disable network"); - } - disabledNetwork = wifiManagerDisableNetwork(networkId); - } - - if (disabledNetwork) { - /* - * Reset the loop state if we disabled the network - */ - resetSupplicantLoopState(); - } else if (newState != currentState || - (newState == SupplicantState.DISCONNECTED && isDriverStopped())) { - setSupplicantState(newState); - if (newState == SupplicantState.DORMANT) { - DetailedState newDetailedState; - Message reconnectMsg = obtainMessage(EVENT_DEFERRED_RECONNECT, mLastBssid); - if (mIsScanOnly || mRunState == RUN_STATE_STOPPING) { - newDetailedState = DetailedState.IDLE; - } else { - newDetailedState = DetailedState.FAILED; - } - handleDisconnectedState(newDetailedState, true); - /** - * If we were associated with a network (networkId != -1), - * assume we reached this state because of a failed attempt - * to acquire an IP address, and attempt another connection - * and IP address acquisition in RECONNECT_DELAY_MSECS - * milliseconds. - */ - if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) { - sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS); - } else if (mRunState == RUN_STATE_STOPPING) { - stopDriver(); - } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) { - reconnectCommand(); - } - } else if (newState == SupplicantState.DISCONNECTED) { - mNetworkProperties.clear(); - mHaveIpAddress = false; - if (isDriverStopped() || mDisconnectExpected) { - handleDisconnectedState(DetailedState.DISCONNECTED, true); - } else { - scheduleDisconnect(); - } - } else if (newState != SupplicantState.COMPLETED && !mDisconnectPending) { - /** - * Ignore events that don't change the connectivity state, - * such as WPA rekeying operations. - */ - if (!(currentState == SupplicantState.COMPLETED && - (newState == SupplicantState.ASSOCIATING || - newState == SupplicantState.ASSOCIATED || - newState == SupplicantState.FOUR_WAY_HANDSHAKE || - newState == SupplicantState.GROUP_HANDSHAKE))) { - setDetailedState(WifiInfo.getDetailedStateOf(newState)); - } - } - - mDisconnectExpected = false; - intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)newState); - if (failedToAuthenticate) { - if (LOCAL_LOGD) Log.d(TAG, "Failed to authenticate, disabling network " + networkId); - wifiManagerDisableNetwork(networkId); - intent.putExtra( - WifiManager.EXTRA_SUPPLICANT_ERROR, - WifiManager.ERROR_AUTHENTICATING); - } - mContext.sendStickyBroadcast(intent); - } - break; - - case EVENT_NETWORK_STATE_CHANGED: - /* - * Each CONNECT or DISCONNECT generates a pair of events. - * One is a supplicant state change event, and the other - * is a network state change event. For connects, the - * supplicant event always arrives first, followed by - * the network state change event. Only the latter event - * has the BSSID, which we are interested in capturing. - * For disconnects, the order is the opposite -- the - * network state change event comes first, followed by - * the supplicant state change event. - */ - NetworkStateChangeResult result = - (NetworkStateChangeResult) msg.obj; - - // Wi-Fi network state changed: - // [31- 6] Reserved for future use - // [ 5- 0] Detailed state ordinal (as defined by NetworkInfo.DetailedState) - eventLogParam = (result.state.ordinal() & 0x3f); - EventLog.writeEvent(EVENTLOG_NETWORK_STATE_CHANGED, eventLogParam); - - if (LOCAL_LOGD) Log.v(TAG, "New network state is " + result.state); - /* - * If we're in scan-only mode, don't advance the state machine, and - * don't report the state change to clients. - */ - if (mIsScanOnly) { - if (LOCAL_LOGD) Log.v(TAG, "Dropping event in scan-only mode"); - break; - } - if (result.state != DetailedState.SCANNING) { - /* - * Reset the scan count since there was a network state - * change. This could be from supplicant trying to associate - * with a network. - */ - mNumScansSinceNetworkStateChange = 0; - } - /* - * If the supplicant sent us a CONNECTED event, we don't - * want to send out an indication of overall network - * connectivity until we have our IP address. If the - * supplicant sent us a DISCONNECTED event, we delay - * sending a notification in case a reconnection to - * the same access point occurs within a short time. - */ - if (result.state == DetailedState.DISCONNECTED) { - if (mWifiInfo.getSupplicantState() != SupplicantState.DORMANT) { - scheduleDisconnect(); - } - break; - } - requestConnectionStatus(mWifiInfo); - if (!(result.state == DetailedState.CONNECTED && - (!mHaveIpAddress || mDisconnectPending))) { - setDetailedState(result.state); - } - - if (result.state == DetailedState.CONNECTED) { - /* - * Remove the 'available networks' notification when we - * successfully connect to a network. - */ - setNotificationVisible(false, 0, false, 0); - boolean wasDisconnectPending = mDisconnectPending; - cancelDisconnect(); - /* - * The connection is fully configured as far as link-level - * connectivity is concerned, but we may still need to obtain - * an IP address. - */ - if (wasDisconnectPending) { - DetailedState saveState = getNetworkInfo().getDetailedState(); - handleDisconnectedState(DetailedState.DISCONNECTED, false); - setDetailedStateInternal(saveState); - } - - configureInterface(); - mLastBssid = result.BSSID; - mLastSsid = mWifiInfo.getSSID(); - mLastNetworkId = result.networkId; - if (mHaveIpAddress) { - setDetailedState(DetailedState.CONNECTED); - } else { - setDetailedState(DetailedState.OBTAINING_IPADDR); - } - } - sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID()); - break; - - case EVENT_SCAN_RESULTS_AVAILABLE: - if (ActivityManagerNative.isSystemReady()) { - mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); - } - sendScanResultsAvailable(); - /** - * On receiving the first scan results after connecting to - * the supplicant, switch scan mode over to passive. - */ - setScanMode(false); - break; - - case EVENT_POLL_INTERVAL: - if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { - requestPolledInfo(mWifiInfo, true); - checkPollTimer(); - } - break; - - case EVENT_DEFERRED_DISCONNECT: - if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { - handleDisconnectedState(DetailedState.DISCONNECTED, true); - } - break; - - case EVENT_DEFERRED_RECONNECT: - /** - * mLastBssid can be null when there is a reconnect - * request on the first BSSID we connect to - */ - String BSSID = (msg.obj != null) ? msg.obj.toString() : null; - /** - * If we've exceeded the maximum number of retries for reconnecting - * to a given network, disable the network - */ - if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { - if (++mReconnectCount > getMaxDhcpRetries()) { - if (LOCAL_LOGD) { - Log.d(TAG, "Failed reconnect count: " + - mReconnectCount + " Disabling " + BSSID); - } - mWM.disableNetwork(mLastNetworkId); - } - reconnectCommand(); - } - break; - - case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED: - /** - * Since this event is sent from another thread, it might have been - * sent after we closed our connection to the supplicant in the course - * of disabling Wi-Fi. In that case, we should just ignore the event. - */ - if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED) { - break; - } - mReconnectCount = 0; - mHaveIpAddress = true; - configureNetworkProperties(); - mObtainingIpAddress = false; - mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); - mLastSignalLevel = -1; // force update of signal strength - if (mNetworkInfo.getDetailedState() != DetailedState.CONNECTED) { - setDetailedState(DetailedState.CONNECTED); - sendNetworkStateChangeBroadcast(mWifiInfo.getBSSID()); - } else { - mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED); - } - if (LOCAL_LOGD) Log.v(TAG, "IP configuration: " + mDhcpInfo); - // Wi-Fi interface configuration state changed: - // [31- 1] Reserved for future use - // [ 0- 0] Interface configuration succeeded (1) or failed (0) - EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 1); - - // We've connected successfully, so allow the notification again in the future - resetNotificationTimer(); - break; - - case EVENT_INTERFACE_CONFIGURATION_FAILED: - if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) { - // Wi-Fi interface configuration state changed: - // [31- 1] Reserved for future use - // [ 0- 0] Interface configuration succeeded (1) or failed (0) - EventLog.writeEvent(EVENTLOG_INTERFACE_CONFIGURATION_STATE_CHANGED, 0); - mNetworkProperties.clear(); - mHaveIpAddress = false; - mWifiInfo.setIpAddress(0); - mObtainingIpAddress = false; - disconnect(); - } - break; - - case EVENT_DRIVER_STATE_CHANGED: - // Wi-Fi driver state changed: - // 0 STARTED - // 1 STOPPED - // 2 HUNG - EventLog.writeEvent(EVENTLOG_DRIVER_STATE_CHANGED, msg.arg1); - - switch (msg.arg1) { - case DRIVER_STARTED: - /** - * Set the number of allowed radio channels according - * to the system setting, since it gets reset by the - * driver upon changing to the STARTED state. - */ - setNumAllowedChannels(); - synchronized (this) { - if (mRunState == RUN_STATE_STARTING) { - mRunState = RUN_STATE_RUNNING; - if (!mIsScanOnly) { - reconnectCommand(); - } else { - // In some situations, supplicant needs to be kickstarted to - // start the background scanning - scan(true); - } - } - } - break; - case DRIVER_HUNG: - Log.e(TAG, "Wifi Driver reports HUNG - reloading."); - /** - * restart the driver - toggle off and on - */ - mWM.setWifiEnabled(false); - mWM.setWifiEnabled(true); - break; - } - noteRunState(); - break; - - case EVENT_PASSWORD_KEY_MAY_BE_INCORRECT: - mPasswordKeyMayBeIncorrect = true; - break; - } - } - - private boolean wifiManagerDisableNetwork(int networkId) { - boolean disabledNetwork = false; - if (0 <= networkId) { - disabledNetwork = mWM.disableNetwork(networkId); - if (LOCAL_LOGD) { - if (disabledNetwork) { - Log.v(TAG, "Disabled network: " + networkId); - } - } - } - if (LOCAL_LOGD) { - if (!disabledNetwork) { - Log.e(TAG, "Failed to disable network:" + - " invalid network id: " + networkId); - } - } - return disabledNetwork; - } - - - private void configureNetworkProperties() { - try { - mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName)); - } catch (SocketException e) { - Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName + - ". e=" + e); - return; - } catch (NullPointerException e) { - Log.e(TAG, "NPE creating NetworkInterface. e=" + e); - return; - } - // TODO - fix this for v6 - try { - mNetworkProperties.addAddress(InetAddress.getByAddress( - NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress))); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e); - } - - try { - mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray( - mDhcpInfo.gateway))); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e); - } - - try { - mNetworkProperties.addDns(InetAddress.getByAddress( - NetworkUtils.v4IntToArray(mDhcpInfo.dns1))); - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e); - } - try { - mNetworkProperties.addDns(InetAddress.getByAddress( - NetworkUtils.v4IntToArray(mDhcpInfo.dns2))); - - } catch (UnknownHostException e) { - Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e); - } - // TODO - add proxy info - } - - private void configureInterface() { - checkPollTimer(); - mLastSignalLevel = -1; - if (!mUseStaticIp) { - if (!mHaveIpAddress && !mObtainingIpAddress) { - mObtainingIpAddress = true; - mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START); - } - } else { - int event; - if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { - event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; - if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration succeeded"); - } else { - event = EVENT_INTERFACE_CONFIGURATION_FAILED; - if (LOCAL_LOGD) Log.v(TAG, "Static IP configuration failed"); - } - sendEmptyMessage(event); - } + public int getDefaultGatewayAddr() { + return mDefaultGatewayAddr.get(); } /** - * Reset our IP state and send out broadcasts following a disconnect. - * @param newState the {@code DetailedState} to set. Should be either - * {@code DISCONNECTED} or {@code FAILED}. - * @param disableInterface indicates whether the interface should - * be disabled + * Check if default route is set */ - private void handleDisconnectedState(DetailedState newState, boolean disableInterface) { - if (mDisconnectPending) { - cancelDisconnect(); - } - mDisconnectExpected = false; - resetConnections(disableInterface); - setDetailedState(newState); - sendNetworkStateChangeBroadcast(mLastBssid); - mWifiInfo.setBSSID(null); - mLastBssid = null; - mLastSsid = null; - mDisconnectPending = false; + public boolean isDefaultRouteSet() { + return mDefaultRouteSet.get(); } /** - * Resets the Wi-Fi Connections by clearing any state, resetting any sockets - * using the interface, stopping DHCP, and disabling the interface. + * Set a flag indicating default route is set for the network */ - public void resetConnections(boolean disableInterface) { - if (LOCAL_LOGD) Log.d(TAG, "Reset connections and stopping DHCP"); - mNetworkProperties.clear(); - mHaveIpAddress = false; - mObtainingIpAddress = false; - mWifiInfo.setIpAddress(0); - - /* - * Reset connection depends on both the interface and the IP assigned, - * so it should be done before any chance of the IP being lost. - */ - NetworkUtils.resetConnections(mInterfaceName); - - // Stop DHCP - if (mDhcpTarget != null) { - mDhcpTarget.setCancelCallback(true); - mDhcpTarget.removeMessages(EVENT_DHCP_START); - } - if (!NetworkUtils.stopDhcp(mInterfaceName)) { - Log.e(TAG, "Could not stop DHCP"); - } - - /** - * Interface is re-enabled in the supplicant - * when moving out of ASSOCIATING state - */ - if(disableInterface) { - if (LOCAL_LOGD) Log.d(TAG, "Disabling interface"); - NetworkUtils.disableInterface(mInterfaceName); - } + public void defaultRouteSet(boolean enabled) { + mDefaultRouteSet.set(enabled); } /** - * The supplicant is reporting that we are disconnected from the current - * access point. Often, however, a disconnect will be followed very shortly - * by a reconnect to the same access point. Therefore, we delay resetting - * the connection's IP state for a bit. + * Return the system properties name associated with the tcp buffer sizes + * for this network. */ - private void scheduleDisconnect() { - mDisconnectPending = true; - if (!hasMessages(EVENT_DEFERRED_DISCONNECT)) { - sendEmptyMessageDelayed(EVENT_DEFERRED_DISCONNECT, DISCONNECT_DELAY_MSECS); - } - } - - private void cancelDisconnect() { - mDisconnectPending = false; - removeMessages(EVENT_DEFERRED_DISCONNECT); - } - - public DhcpInfo getDhcpInfo() { - return mDhcpInfo; + public String getTcpBufferSizesPropName() { + return "net.tcp.buffersize.wifi"; } - public synchronized List<ScanResult> getScanResultsList() { - return mScanResults; - } - public synchronized void setScanResultsList(List<ScanResult> scanList) { - mScanResults = scanList; - } + /********************************************************* + * Methods exposed for public use + ********************************************************/ /** - * Get status information for the current connection, if any. - * @return a {@link WifiInfo} object containing information about the current connection + * TODO: doc */ - public WifiInfo requestConnectionInfo() { - requestConnectionStatus(mWifiInfo); - requestPolledInfo(mWifiInfo, false); - return mWifiInfo; - } - - private void requestConnectionStatus(WifiInfo info) { - String reply = status(); - if (reply == null) { - return; - } - /* - * Parse the reply from the supplicant to the status command, and update - * local state accordingly. The reply is a series of lines of the form - * "name=value". - */ - String SSID = null; - String BSSID = null; - String suppState = null; - int netId = -1; - String[] lines = reply.split("\n"); - for (String line : lines) { - String[] prop = line.split(" *= *", 2); - if (prop.length < 2) - continue; - String name = prop[0]; - String value = prop[1]; - if (name.equalsIgnoreCase("id")) - netId = Integer.parseInt(value); - else if (name.equalsIgnoreCase("ssid")) - SSID = value; - else if (name.equalsIgnoreCase("bssid")) - BSSID = value; - else if (name.equalsIgnoreCase("wpa_state")) - suppState = value; - } - info.setNetworkId(netId); - info.setSSID(SSID); - info.setBSSID(BSSID); - /* - * We only set the supplicant state if the previous state was - * UNINITIALIZED. This should only happen when we first connect to - * the supplicant. Once we're connected, we should always receive - * an event upon any state change, but in this case, we want to - * make sure any listeners are made aware of the state change. - */ - if (mWifiInfo.getSupplicantState() == SupplicantState.UNINITIALIZED && suppState != null) - setSupplicantState(suppState); + public boolean pingSupplicant() { + return sendSyncMessage(CMD_PING_SUPPLICANT).boolValue; } /** - * Get the dynamic information that is not reported via events. - * @param info the object into which the information should be captured. + * TODO: doc */ - private synchronized void requestPolledInfo(WifiInfo info, boolean polling) - { - int newRssi = (polling ? getRssiApprox() : getRssi()); - if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values - /* some implementations avoid negative values by adding 256 - * so we need to adjust for that here. - */ - if (newRssi > 0) newRssi -= 256; - info.setRssi(newRssi); - /* - * Rather then sending the raw RSSI out every time it - * changes, we precalculate the signal level that would - * be displayed in the status bar, and only send the - * broadcast if that much more coarse-grained number - * changes. This cuts down greatly on the number of - * broadcasts, at the cost of not informing others - * interested in RSSI of all the changes in signal - * level. - */ - // TODO: The second arg to the call below needs to be a symbol somewhere, but - // it's actually the size of an array of icons that's private - // to StatusBar Policy. - int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4); - if (newSignalLevel != mLastSignalLevel) { - sendRssiChangeBroadcast(newRssi); - } - mLastSignalLevel = newSignalLevel; - } else { - info.setRssi(-200); - } - int newLinkSpeed = getLinkSpeed(); - if (newLinkSpeed != -1) { - info.setLinkSpeed(newLinkSpeed); - } - } - - private void sendRssiChangeBroadcast(final int newRssi) { - if (ActivityManagerNative.isSystemReady()) { - Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION); - intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi); - mContext.sendBroadcast(intent); - } - } - - private void sendNetworkStateChangeBroadcast(String bssid) { - Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT - | Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo); - if (bssid != null) - intent.putExtra(WifiManager.EXTRA_BSSID, bssid); - mContext.sendStickyBroadcast(intent); + public boolean startScan(boolean forceActive) { + return sendSyncMessage(obtainMessage(CMD_START_SCAN, forceActive ? + SCAN_ACTIVE : SCAN_PASSIVE, 0)).boolValue; } /** - * Disable Wi-Fi connectivity by stopping the driver. + * TODO: doc */ - public boolean teardown() { - if (!mTornDownByConnMgr) { - if (disconnectAndStop()) { - setTornDownByConnMgr(true); - return true; - } else { - return false; - } + public void setWifiEnabled(boolean enable) { + mLastEnableUid.set(Binder.getCallingUid()); + if (enable) { + /* Argument is the state that is entered prior to load */ + sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0)); + sendMessage(CMD_START_SUPPLICANT); } else { - return true; + sendMessage(CMD_STOP_SUPPLICANT); + /* Argument is the state that is entered upon success */ + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0)); } } /** - * Reenable Wi-Fi connectivity by restarting the driver. + * TODO: doc */ - public boolean reconnect() { - if (mTornDownByConnMgr) { - if (restart()) { - setTornDownByConnMgr(false); - return true; - } else { - return false; - } + public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable) { + mLastApEnableUid.set(Binder.getCallingUid()); + if (enable) { + /* Argument is the state that is entered prior to load */ + sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0)); + sendMessage(obtainMessage(CMD_START_AP, wifiConfig)); } else { - return true; + sendMessage(CMD_STOP_AP); + /* Argument is the state that is entered upon success */ + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_DISABLED, 0)); } } /** - * We want to stop the driver, but if we're connected to a network, - * we first want to disconnect, so that the supplicant is always in - * a known state (DISCONNECTED) when the driver is stopped. - * @return {@code true} if the operation succeeds, which means that the - * disconnect or stop command was initiated. + * TODO: doc */ - public synchronized boolean disconnectAndStop() { - boolean ret = true;; - if (mRunState != RUN_STATE_STOPPING && mRunState != RUN_STATE_STOPPED) { - // Take down any open network notifications - setNotificationVisible(false, 0, false, 0); - - if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) { - ret = stopDriver(); - } else { - ret = disconnect(); - } - mRunState = RUN_STATE_STOPPING; - } - return ret; - } - - public synchronized boolean restart() { - if (mRunState == RUN_STATE_STOPPED) { - mRunState = RUN_STATE_STARTING; - resetConnections(true); - return startDriver(); - } else if (mRunState == RUN_STATE_STOPPING) { - mRunState = RUN_STATE_STARTING; - } - return true; - } - public int getWifiState() { return mWifiState.get(); } - public void setWifiState(int wifiState) { - mWifiState.set(wifiState); - } - - /** - * The WifiNative interface functions are listed below. - * The only native call that is not synchronized on - * WifiStateTracker is waitForEvent() which waits on a - * seperate monitor channel. - * - * All supplicant commands need the wifi to be in an - * enabled state. This can be done by checking the - * mWifiState to be WIFI_STATE_ENABLED. - * - * All commands that can cause commands to driver - * initiated need the driver state to be started. - * This is done by checking isDriverStopped() to - * be false. - */ - /** - * Load the driver and firmware - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * TODO: doc */ - public synchronized boolean loadDriver() { - return WifiNative.loadDriver(); + public String getWifiStateByName() { + switch (mWifiState.get()) { + case WIFI_STATE_DISABLING: + return "disabling"; + case WIFI_STATE_DISABLED: + return "disabled"; + case WIFI_STATE_ENABLING: + return "enabling"; + case WIFI_STATE_ENABLED: + return "enabled"; + case WIFI_STATE_UNKNOWN: + return "unknown state"; + default: + return "[invalid state]"; + } } /** - * Unload the driver and firmware - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * TODO: doc */ - public synchronized boolean unloadDriver() { - return WifiNative.unloadDriver(); + public int getWifiApState() { + return mWifiApState.get(); } /** - * Check the supplicant config and - * start the supplicant daemon - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * TODO: doc */ - public synchronized boolean startSupplicant() { - return WifiNative.startSupplicant(); + public String getWifiApStateByName() { + switch (mWifiApState.get()) { + case WIFI_AP_STATE_DISABLING: + return "disabling"; + case WIFI_AP_STATE_DISABLED: + return "disabled"; + case WIFI_AP_STATE_ENABLING: + return "enabling"; + case WIFI_AP_STATE_ENABLED: + return "enabled"; + case WIFI_AP_STATE_FAILED: + return "failed"; + default: + return "[invalid state]"; + } } /** - * Stop the supplicant daemon + * Get status information for the current connection, if any. + * @return a {@link WifiInfo} object containing information about the current connection * - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean stopSupplicant() { - return WifiNative.stopSupplicant(); + public WifiInfo requestConnectionInfo() { + return mWifiInfo; } - /** - * Establishes two channels - control channel for commands - * and monitor channel for notifying WifiMonitor - * - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public synchronized boolean connectToSupplicant() { - return WifiNative.connectToSupplicant(); + public DhcpInfo getDhcpInfo() { + return mDhcpInfo; } /** - * Close the control/monitor channels to supplicant + * TODO: doc */ - public synchronized void closeSupplicantConnection() { - WifiNative.closeSupplicantConnection(); + public void startWifi(boolean enable) { + if (enable) { + sendMessage(CMD_START_DRIVER); + } else { + sendMessage(CMD_STOP_DRIVER); + } } /** - * Check if the supplicant is alive - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * TODO: doc */ - public synchronized boolean ping() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.pingCommand(); + public void disconnectAndStop() { + sendMessage(CMD_DISCONNECT); + sendMessage(CMD_STOP_DRIVER); } /** - * initiate an active or passive scan - * - * @param forceActive true if it is a active scan - * @return {@code true} if the operation succeeds, {@code false} otherwise + * TODO: doc */ - public synchronized boolean scan(boolean forceActive) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.scanCommand(forceActive); + public void setScanOnlyMode(boolean enable) { + if (enable) { + sendMessage(obtainMessage(CMD_SET_SCAN_MODE, SCAN_ONLY_MODE, 0)); + } else { + sendMessage(obtainMessage(CMD_SET_SCAN_MODE, CONNECT_MODE, 0)); + } } /** - * Specifies whether the supplicant or driver - * take care of initiating scan and doing AP selection - * - * @param mode - * SUPPL_SCAN_HANDLING_NORMAL - * SUPPL_SCAN_HANDLING_LIST_ONLY - * @return {@code true} if the operation succeeds, {@code false} otherwise + * TODO: doc */ - public synchronized boolean setScanResultHandling(int mode) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.setScanResultHandlingCommand(mode); + public void setScanType(boolean active) { + if (active) { + sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_ACTIVE, 0)); + } else { + sendMessage(obtainMessage(CMD_SET_SCAN_TYPE, SCAN_PASSIVE, 0)); + } } /** - * Fetch the scan results from the supplicant - * - * @return example result string - * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1 - * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2 + * TODO: doc */ - public synchronized String scanResults() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return null; - } - return WifiNative.scanResultsCommand(); + public List<ScanResult> getScanResultsList() { + return mScanResults; } /** - * Set the scan mode - active or passive - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * Disconnect from Access Point */ - public synchronized boolean setScanMode(boolean isScanModeActive) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - if (mIsScanModeActive != isScanModeActive) { - return WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive); - } - return true; + public boolean disconnectCommand() { + return sendSyncMessage(CMD_DISCONNECT).boolValue; } /** - * Disconnect from Access Point - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * Initiate a reconnection to AP */ - public synchronized boolean disconnect() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.disconnectCommand(); + public boolean reconnectCommand() { + return sendSyncMessage(CMD_RECONNECT).boolValue; } /** - * Initiate a reconnection to AP - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * Initiate a re-association to AP */ - public synchronized boolean reconnectCommand() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.reconnectCommand(); + public boolean reassociateCommand() { + return sendSyncMessage(CMD_REASSOCIATE).boolValue; } /** - * Add a network + * Add a network synchronously * * @return network id of the new network */ - public synchronized int addNetwork() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return -1; - } - return WifiNative.addNetworkCommand(); + public int addOrUpdateNetwork(WifiConfiguration config) { + return sendSyncMessage(CMD_ADD_OR_UPDATE_NETWORK, config).intValue; + } + + public List<WifiConfiguration> getConfiguredNetworks() { + return sendSyncMessage(CMD_GET_NETWORK_CONFIG).configList; } /** * Delete a network * * @param networkId id of the network to be removed - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean removeNetwork(int networkId) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId); + public boolean removeNetwork(int networkId) { + return sendSyncMessage(obtainMessage(CMD_REMOVE_NETWORK, networkId, 0)).boolValue; } + private class EnableNetParams { + private int netId; + private boolean disableOthers; + EnableNetParams(int n, boolean b) { + netId = n; + disableOthers = b; + } + } /** * Enable a network * @@ -1818,11 +923,9 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * @param disableOthers true, if all other networks have to be disabled * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean enableNetwork(int netId, boolean disableOthers) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.enableNetworkCommand(netId, disableOthers); + public boolean enableNetwork(int netId, boolean disableOthers) { + return sendSyncMessage(CMD_ENABLE_NETWORK, + new EnableNetParams(netId, disableOthers)).boolValue; } /** @@ -1831,23 +934,8 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * @param netId network id of the network * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean disableNetwork(int netId) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.disableNetworkCommand(netId); - } - - /** - * Initiate a re-association in supplicant - * - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public synchronized boolean reassociate() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.reassociateCommand(); + public boolean disableNetwork(int netId) { + return sendSyncMessage(obtainMessage(CMD_DISABLE_NETWORK, netId, 0)).boolValue; } /** @@ -1855,66 +943,17 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * alternate APs to connect * * @param bssid BSSID of the network - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean addToBlacklist(String bssid) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.addToBlacklistCommand(bssid); + public void addToBlacklist(String bssid) { + sendMessage(obtainMessage(CMD_BLACKLIST_NETWORK, bssid)); } /** * Clear the blacklist list * - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean clearBlacklist() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.clearBlacklistCommand(); - } - - /** - * List all configured networks - * - * @return list of networks or null on failure - */ - public synchronized String listNetworks() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return null; - } - return WifiNative.listNetworksCommand(); - } - - /** - * Get network setting by name - * - * @param netId network id of the network - * @param name network variable key - * @return value corresponding to key - */ - public synchronized String getNetworkVariable(int netId, String name) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return null; - } - return WifiNative.getNetworkVariableCommand(netId, name); - } - - /** - * Set network setting by name - * - * @param netId network id of the network - * @param name network variable key - * @param value network variable value - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public synchronized boolean setNetworkVariable(int netId, String name, String value) { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.setNetworkVariableCommand(netId, name, value); + public void clearBlacklist() { + sendMessage(obtainMessage(CMD_CLEAR_BLACKLIST)); } /** @@ -1930,23 +969,20 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * wpa_state=COMPLETED * ip_address=X.X.X.X */ - public synchronized String status() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return null; - } - return WifiNative.statusCommand(); + public String status() { + return sendSyncMessage(CMD_CONNECTION_STATUS).stringValue; } + public void enableRssiPolling(boolean enabled) { + sendMessage(obtainMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0)); + } /** * Get RSSI to currently connected network * * @return RSSI value, -1 on failure */ - public synchronized int getRssi() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return -1; - } - return WifiNative.getRssiApproxCommand(); + public int getRssi() { + return sendSyncMessage(CMD_GET_RSSI).intValue; } /** @@ -1954,11 +990,8 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * * @return RSSI value, -1 on failure */ - public synchronized int getRssiApprox() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return -1; - } - return WifiNative.getRssiApproxCommand(); + public int getRssiApprox() { + return sendSyncMessage(CMD_GET_RSSI_APPROX).intValue; } /** @@ -1966,11 +999,8 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * * @return link speed, -1 on failure */ - public synchronized int getLinkSpeed() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return -1; - } - return WifiNative.getLinkSpeedCommand(); + public int getLinkSpeed() { + return sendSyncMessage(CMD_GET_LINK_SPEED).intValue; } /** @@ -1978,72 +1008,22 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * * @return MAC address, null on failure */ - public synchronized String getMacAddress() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return null; - } - return WifiNative.getMacAddressCommand(); - } - - /** - * Start driver - * - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public synchronized boolean startDriver() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; - } - return WifiNative.startDriverCommand(); - } - - /** - * Stop driver - * - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public synchronized boolean stopDriver() { - /* Driver stop should not happen only when supplicant event - * DRIVER_STOPPED has already been handled */ - if (mWifiState.get() != WIFI_STATE_ENABLED || mRunState == RUN_STATE_STOPPED) { - return false; - } - return WifiNative.stopDriverCommand(); + public String getMacAddress() { + return sendSyncMessage(CMD_GET_MAC_ADDR).stringValue; } /** * Start packet filtering - * - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean startPacketFiltering() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.startPacketFiltering(); + public void startPacketFiltering() { + sendMessage(CMD_START_PACKET_FILTERING); } /** * Stop packet filtering - * - * @return {@code true} if the operation succeeds, {@code false} otherwise - */ - public synchronized boolean stopPacketFiltering() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.stopPacketFiltering(); - } - - /** - * Get power mode - * @return power mode */ - public synchronized int getPowerMode() { - if (mWifiState.get() != WIFI_STATE_ENABLED && !isDriverStopped()) { - return -1; - } - return WifiNative.getPowerModeCommand(); + public void stopPacketFiltering() { + sendMessage(CMD_STOP_PACKET_FILTERING); } /** @@ -2051,36 +1031,26 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * @param mode * DRIVER_POWER_MODE_AUTO * DRIVER_POWER_MODE_ACTIVE - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean setPowerMode(int mode) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.setPowerModeCommand(mode); + public void setPowerMode(int mode) { + sendMessage(obtainMessage(CMD_SET_POWER_MODE, mode, 0)); } /** * Set the number of allowed radio frequency channels from the system * setting value, if any. - * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., - * the number of channels is invalid. */ - public synchronized boolean setNumAllowedChannels() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } + public void setNumAllowedChannels() { try { - return setNumAllowedChannels( + setNumAllowedChannels( Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS)); } catch (Settings.SettingNotFoundException e) { if (mNumAllowedChannels != 0) { - WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels); + setNumAllowedChannels(mNumAllowedChannels); } // otherwise, use the driver default } - return true; } /** @@ -2088,27 +1058,21 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * in the current regulatory domain. * @param numChannels the number of allowed channels. Must be greater than 0 * and less than or equal to 16. - * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., - * {@code numChannels} is outside the valid range. */ - public synchronized boolean setNumAllowedChannels(int numChannels) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - mNumAllowedChannels = numChannels; - return WifiNative.setNumAllowedChannelsCommand(numChannels); + public void setNumAllowedChannels(int numChannels) { + sendMessage(obtainMessage(CMD_SET_NUM_ALLOWED_CHANNELS, numChannels, 0)); } /** * Get number of allowed channels * * @return channel count, -1 on failure + * + * TODO: this is not a public API and needs to be removed in favor + * of asynchronous reporting. unused for now. */ - public synchronized int getNumAllowedChannels() { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return -1; - } - return WifiNative.getNumAllowedChannelsCommand(); + public int getNumAllowedChannels() { + return -1; } /** @@ -2118,13 +1082,9 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * BLUETOOTH_COEXISTENCE_MODE_ENABLED * BLUETOOTH_COEXISTENCE_MODE_DISABLED * BLUETOOTH_COEXISTENCE_MODE_SENSE - * @return {@code true} if the operation succeeds, {@code false} otherwise */ - public synchronized boolean setBluetoothCoexistenceMode(int mode) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return false; - } - return WifiNative.setBluetoothCoexistenceModeCommand(mode); + public void setBluetoothCoexistenceMode(int mode) { + sendMessage(obtainMessage(CMD_SET_BLUETOOTH_COEXISTENCE, mode, 0)); } /** @@ -2134,75 +1094,292 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * * @param isBluetoothPlaying whether to enable or disable this mode */ - public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) { - if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) { - return; - } - WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying); + public void setBluetoothScanMode(boolean isBluetoothPlaying) { + sendMessage(obtainMessage(CMD_SET_BLUETOOTH_SCAN_MODE, isBluetoothPlaying ? 1 : 0, 0)); } /** * Save configuration on supplicant * * @return {@code true} if the operation succeeds, {@code false} otherwise + * + * TODO: deprecate this */ - public synchronized boolean saveConfig() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; + public boolean saveConfig() { + return sendSyncMessage(CMD_SAVE_CONFIG).boolValue; + } + + /** + * TODO: doc + */ + public void requestCmWakeLock() { + sendMessage(CMD_REQUEST_CM_WAKELOCK); + } + + /********************************************************* + * Internal private functions + ********************************************************/ + + class SyncReturn { + boolean boolValue; + int intValue; + String stringValue; + Object objValue; + List<WifiConfiguration> configList; + } + + class SyncParams { + Object mParameter; + SyncReturn mSyncReturn; + SyncParams() { + mSyncReturn = new SyncReturn(); + } + SyncParams(Object p) { + mParameter = p; + mSyncReturn = new SyncReturn(); } - return WifiNative.saveConfigCommand(); } /** - * Reload the configuration from file - * - * @return {@code true} if the operation succeeds, {@code false} otherwise + * message.arg2 is reserved to indicate synchronized + * message.obj is used to store SyncParams */ - public synchronized boolean reloadConfig() { - if (mWifiState.get() != WIFI_STATE_ENABLED) { - return false; + private SyncReturn syncedSend(Message msg) { + SyncParams syncParams = (SyncParams) msg.obj; + msg.arg2 = SYNCHRONOUS_CALL; + synchronized(syncParams) { + if (DBG) Log.d(TAG, "syncedSend " + msg); + sendMessage(msg); + try { + syncParams.wait(); + } catch (InterruptedException e) { + Log.e(TAG, "sendSyncMessage: unexpected interruption of wait()"); + return null; + } } - return WifiNative.reloadConfigCommand(); + return syncParams.mSyncReturn; } - public boolean setRadio(boolean turnOn) { - return mWM.setWifiEnabled(turnOn); + private SyncReturn sendSyncMessage(Message msg) { + SyncParams syncParams = new SyncParams(); + msg.obj = syncParams; + return syncedSend(msg); + } + + private SyncReturn sendSyncMessage(int what, Object param) { + SyncParams syncParams = new SyncParams(param); + Message msg = obtainMessage(what, syncParams); + return syncedSend(msg); + } + + + private SyncReturn sendSyncMessage(int what) { + return sendSyncMessage(obtainMessage(what)); + } + + private void notifyOnMsgObject(Message msg) { + SyncParams syncParams = (SyncParams) msg.obj; + if (syncParams != null) { + synchronized(syncParams) { + if (DBG) Log.d(TAG, "notifyOnMsgObject " + msg); + syncParams.notify(); + } + } + else { + Log.e(TAG, "Error! syncParams in notifyOnMsgObject is null"); + } + } + + private void setWifiState(int wifiState) { + final int previousWifiState = mWifiState.get(); + + try { + if (wifiState == WIFI_STATE_ENABLED) { + mBatteryStats.noteWifiOn(mLastEnableUid.get()); + } else if (wifiState == WIFI_STATE_DISABLED) { + mBatteryStats.noteWifiOff(mLastEnableUid.get()); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to note battery stats in wifi"); + } + + mWifiState.set(wifiState); + + if (DBG) Log.d(TAG, "setWifiState: " + getWifiStateByName()); + + if (!ActivityManagerNative.isSystemReady()) return; + + final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_STATE, wifiState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, previousWifiState); + mContext.sendStickyBroadcast(intent); + } + + private void setWifiApState(int wifiApState) { + final int previousWifiApState = mWifiApState.get(); + + try { + if (wifiApState == WIFI_AP_STATE_ENABLED) { + mBatteryStats.noteWifiOn(mLastApEnableUid.get()); + } else if (wifiApState == WIFI_AP_STATE_DISABLED) { + mBatteryStats.noteWifiOff(mLastApEnableUid.get()); + } + } catch (RemoteException e) { + Log.d(TAG, "Failed to note battery stats in wifi"); + } + + // Update state + mWifiApState.set(wifiApState); + + if (DBG) Log.d(TAG, "setWifiApState: " + getWifiApStateByName()); + + // Broadcast + if (!ActivityManagerNative.isSystemReady()) return; + final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState); + intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState); + mContext.sendStickyBroadcast(intent); } /** - * {@inheritDoc} - * There are currently no Wi-Fi-specific features supported. - * @param feature the name of the feature - * @return {@code -1} indicating failure, always + * Parse the scan result line passed to us by wpa_supplicant (helper). + * @param line the line to parse + * @return the {@link ScanResult} object */ - public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { - return -1; + private ScanResult parseScanResult(String line) { + ScanResult scanResult = null; + if (line != null) { + /* + * Cache implementation (LinkedHashMap) is not synchronized, thus, + * must synchronized here! + */ + synchronized (mScanResultCache) { + String[] result = scanResultPattern.split(line); + if (3 <= result.length && result.length <= 5) { + String bssid = result[0]; + // bssid | frequency | level | flags | ssid + int frequency; + int level; + try { + frequency = Integer.parseInt(result[1]); + level = Integer.parseInt(result[2]); + /* some implementations avoid negative values by adding 256 + * so we need to adjust for that here. + */ + if (level > 0) level -= 256; + } catch (NumberFormatException e) { + frequency = 0; + level = 0; + } + + /* + * The formatting of the results returned by + * wpa_supplicant is intended to make the fields + * line up nicely when printed, + * not to make them easy to parse. So we have to + * apply some heuristics to figure out which field + * is the SSID and which field is the flags. + */ + String ssid; + String flags; + if (result.length == 4) { + if (result[3].charAt(0) == '[') { + flags = result[3]; + ssid = ""; + } else { + flags = ""; + ssid = result[3]; + } + } else if (result.length == 5) { + flags = result[3]; + ssid = result[4]; + } else { + // Here, we must have 3 fields: no flags and ssid + // set + flags = ""; + ssid = ""; + } + + // bssid + ssid is the hash key + String key = bssid + ssid; + scanResult = mScanResultCache.get(key); + if (scanResult != null) { + scanResult.level = level; + scanResult.SSID = ssid; + scanResult.capabilities = flags; + scanResult.frequency = frequency; + } else { + // Do not add scan results that have no SSID set + if (0 < ssid.trim().length()) { + scanResult = + new ScanResult( + ssid, bssid, flags, level, frequency); + mScanResultCache.put(key, scanResult); + } + } + } else { + Log.w(TAG, "Misformatted scan result text with " + + result.length + " fields: " + line); + } + } + } + + return scanResult; } /** - * {@inheritDoc} - * There are currently no Wi-Fi-specific features supported. - * @param feature the name of the feature - * @return {@code -1} indicating failure, always + * scanResults input format + * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1 + * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2 */ - public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { - return -1; - } + private void setScanResults(String scanResults) { + if (scanResults == null) { + return; + } - public void interpretScanResultsAvailable() { + List<ScanResult> scanList = new ArrayList<ScanResult>(); + + int lineCount = 0; + + int scanResultsLen = scanResults.length(); + // Parse the result string, keeping in mind that the last line does + // not end with a newline. + for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) { + if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '\n') { + ++lineCount; + + if (lineCount == 1) { + lineBeg = lineEnd + 1; + continue; + } + if (lineEnd > lineBeg) { + String line = scanResults.substring(lineBeg, lineEnd); + ScanResult scanResult = parseScanResult(line); + if (scanResult != null) { + scanList.add(scanResult); + } else { + Log.w(TAG, "misformatted scan result for: " + line); + } + } + lineBeg = lineEnd + 1; + } + } + + mScanResults = scanList; + } + private void checkAndSetNotification() { // If we shouldn't place a notification on available networks, then // don't bother doing any of the following if (!mNotificationEnabled) return; - NetworkInfo networkInfo = getNetworkInfo(); - - State state = networkInfo.getState(); + State state = mNetworkInfo.getState(); if ((state == NetworkInfo.State.DISCONNECTED) || (state == NetworkInfo.State.UNKNOWN)) { - // Look for an open network - List<ScanResult> scanResults = getScanResultsList(); + List<ScanResult> scanResults = mScanResults; if (scanResults != null) { int numOpenNetworks = 0; for (int i = scanResults.size() - 1; i >= 0; i--) { @@ -2212,7 +1389,7 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { numOpenNetworks++; } } - + if (numOpenNetworks > 0) { if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { /* @@ -2228,21 +1405,12 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { } } } - + // No open networks in range, remove the notification setNotificationVisible(false, 0, false, 0); } /** - * Send a notification that the results of a scan for network access - * points has completed, and results are available. - */ - private void sendScanResultsAvailable() { - Message msg = mTarget.obtainMessage(EVENT_SCAN_RESULTS_AVAILABLE, mNetworkInfo); - msg.sendToTarget(); - } - - /** * Display or don't display a notification that there are open Wi-Fi networks. * @param visible {@code true} if notification should be visible, {@code false} otherwise * @param numNetworks the number networks seen @@ -2251,14 +1419,15 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { * @param delay time in milliseconds after which the notification should be made * visible or invisible. */ - public void setNotificationVisible(boolean visible, int numNetworks, boolean force, int delay) { - + private void setNotificationVisible(boolean visible, int numNetworks, boolean force, + int delay) { + // Since we use auto cancel on the notification, when the // mNetworksAvailableNotificationShown is true, the notification may // have actually been canceled. However, when it is false we know // for sure that it is not being shown (it will not be shown any other // place than here) - + // If it should be hidden and it is already hidden, then noop if (!visible && !mNotificationShown && !force) { return; @@ -2266,12 +1435,12 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { Message message; if (visible) { - + // Not enough time has passed to show the notification again if (System.currentTimeMillis() < mNotificationRepeatTime) { return; } - + if (mNotification == null) { // Cache the Notification mainly so we can remove the // EVENT_NOTIFICATION_CHANGED message with this Notification from @@ -2290,184 +1459,69 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { com.android.internal.R.plurals.wifi_available_detailed, numNetworks); mNotification.tickerText = title; mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); - + mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; - message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1, + message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 1, ICON_NETWORKS_AVAILABLE, mNotification); - + } else { // Remove any pending messages to show the notification - mTarget.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification); - - message = mTarget.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, ICON_NETWORKS_AVAILABLE); + mCsHandler.removeMessages(EVENT_NOTIFICATION_CHANGED, mNotification); + + message = mCsHandler.obtainMessage(EVENT_NOTIFICATION_CHANGED, 0, + ICON_NETWORKS_AVAILABLE); } - mTarget.sendMessageDelayed(message, delay); - - mNotificationShown = visible; - } + mCsHandler.sendMessageDelayed(message, delay); - /** - * Clears variables related to tracking whether a notification has been - * shown recently. - * <p> - * After calling this method, the timer that prevents notifications from - * being shown too often will be cleared. - */ - private void resetNotificationTimer() { - mNotificationRepeatTime = 0; - mNumScansSinceNetworkStateChange = 0; + mNotificationShown = visible; } - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - - sb.append(mNetworkProperties.toString()); - sb.append(" runState="); - if (mRunState >= 1 && mRunState <= mRunStateNames.length) { - sb.append(mRunStateNames[mRunState-1]); - } else { - sb.append(mRunState); + private void configureNetworkProperties() { + try { + mNetworkProperties.setInterface(NetworkInterface.getByName(mInterfaceName)); + } catch (SocketException e) { + Log.e(TAG, "SocketException creating NetworkInterface from " + mInterfaceName + + ". e=" + e); + return; + } catch (NullPointerException e) { + Log.e(TAG, "NPE creating NetworkInterface. e=" + e); + return; } - sb.append(LS).append(mWifiInfo).append(LS); - sb.append(mDhcpInfo).append(LS); - sb.append("haveIpAddress=").append(mHaveIpAddress). - append(", obtainingIpAddress=").append(mObtainingIpAddress). - append(", scanModeActive=").append(mIsScanModeActive).append(LS). - append("lastSignalLevel=").append(mLastSignalLevel). - append(", explicitlyDisabled=").append(mTornDownByConnMgr); - return sb.toString(); - } - - private class DhcpHandler extends Handler { - - private Handler mTarget; - - /** - * Whether to skip the DHCP result callback to the target. For example, - * this could be set if the network we were requesting an IP for has - * since been disconnected. - * <p> - * Note: There is still a chance where the client's intended DHCP - * request not being canceled. For example, we are request for IP on - * A, and he queues request for IP on B, and then cancels the request on - * B while we're still requesting from A. - */ - private boolean mCancelCallback; - - /** - * Instance of the bluetooth headset helper. This needs to be created - * early because there is a delay before it actually 'connects', as - * noted by its javadoc. If we check before it is connected, it will be - * in an error state and we will not disable coexistence. - */ - private BluetoothHeadset mBluetoothHeadset; - - public DhcpHandler(Looper looper, Handler target) { - super(looper); - mTarget = target; - - mBluetoothHeadset = new BluetoothHeadset(mContext, null); + // TODO - fix this for v6 + try { + mNetworkProperties.addAddress(InetAddress.getByAddress( + NetworkUtils.v4IntToArray(mDhcpInfo.ipAddress))); + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting IpAddress using " + mDhcpInfo + ", e=" + e); } - public void handleMessage(Message msg) { - int event; - - switch (msg.what) { - case EVENT_DHCP_START: - - boolean modifiedBluetoothCoexistenceMode = false; - int powerMode = DRIVER_POWER_MODE_AUTO; - - if (shouldDisableCoexistenceMode()) { - /* - * There are problems setting the Wi-Fi driver's power - * mode to active when bluetooth coexistence mode is - * enabled or sense. - * <p> - * We set Wi-Fi to active mode when - * obtaining an IP address because we've found - * compatibility issues with some routers with low power - * mode. - * <p> - * In order for this active power mode to properly be set, - * we disable coexistence mode until we're done with - * obtaining an IP address. One exception is if we - * are currently connected to a headset, since disabling - * coexistence would interrupt that connection. - */ - modifiedBluetoothCoexistenceMode = true; - - // Disable the coexistence mode - setBluetoothCoexistenceMode( - WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); - } - - powerMode = getPowerMode(); - if (powerMode < 0) { - // Handle the case where supplicant driver does not support - // getPowerModeCommand. - powerMode = DRIVER_POWER_MODE_AUTO; - } - if (powerMode != DRIVER_POWER_MODE_ACTIVE) { - setPowerMode(DRIVER_POWER_MODE_ACTIVE); - } - - synchronized (this) { - // A new request is being made, so assume we will callback - mCancelCallback = false; - } - Log.d(TAG, "DhcpHandler: DHCP request started"); - if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { - event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED; - if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded"); - } else { - event = EVENT_INTERFACE_CONFIGURATION_FAILED; - Log.i(TAG, "DhcpHandler: DHCP request failed: " + - NetworkUtils.getDhcpError()); - } - - if (powerMode != DRIVER_POWER_MODE_ACTIVE) { - setPowerMode(powerMode); - } - - if (modifiedBluetoothCoexistenceMode) { - // Set the coexistence mode back to its default value - setBluetoothCoexistenceMode( - WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); - } - - synchronized (this) { - if (!mCancelCallback) { - mTarget.sendEmptyMessage(event); - } - } - break; - } + try { + mNetworkProperties.setGateway(InetAddress.getByAddress(NetworkUtils.v4IntToArray( + mDhcpInfo.gateway))); + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting Gateway using " + mDhcpInfo + ", e=" + e); } - public synchronized void setCancelCallback(boolean cancelCallback) { - mCancelCallback = cancelCallback; + try { + mNetworkProperties.addDns(InetAddress.getByAddress( + NetworkUtils.v4IntToArray(mDhcpInfo.dns1))); + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting Dns1 using " + mDhcpInfo + ", e=" + e); } + try { + mNetworkProperties.addDns(InetAddress.getByAddress( + NetworkUtils.v4IntToArray(mDhcpInfo.dns2))); - /** - * Whether to disable coexistence mode while obtaining IP address. This - * logic will return true only if the current bluetooth - * headset/handsfree state is disconnected. This means if it is in an - * error state, we will NOT disable coexistence mode to err on the side - * of safety. - * - * @return Whether to disable coexistence mode. - */ - private boolean shouldDisableCoexistenceMode() { - int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset()); - return state == BluetoothHeadset.STATE_DISCONNECTED; + } catch (UnknownHostException e) { + Log.e(TAG, "Exception setting Dns2 using " + mDhcpInfo + ", e=" + e); } + // TODO - add proxy info } - + + private void checkUseStaticIp() { mUseStaticIp = false; final ContentResolver cr = mContext.getContentResolver(); @@ -2558,6 +1612,7 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { Settings.System.WIFI_STATIC_DNS2), false, this); } + @Override public void onChange(boolean selfChange) { super.onChange(selfChange); @@ -2587,15 +1642,2304 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { oDns2 != mDhcpInfo.dns2)); if (changed) { - resetConnections(true); - configureInterface(); + sendMessage(CMD_RECONFIGURE_IP); if (mUseStaticIp) { - mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED); + mCsHandler.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED); } } } } + + /** + * Clears variables related to tracking whether a notification has been + * shown recently. + * <p> + * After calling this method, the timer that prevents notifications from + * being shown too often will be cleared. + */ + private void resetNotificationTimer() { + mNotificationRepeatTime = 0; + mNumScansSinceNetworkStateChange = 0; + } + + + /** + * Whether to disable coexistence mode while obtaining IP address. This + * logic will return true only if the current bluetooth + * headset/handsfree state is disconnected. This means if it is in an + * error state, we will NOT disable coexistence mode to err on the side + * of safety. + * + * @return Whether to disable coexistence mode. + */ + private boolean shouldDisableCoexistenceMode() { + int state = mBluetoothHeadset.getState(mBluetoothHeadset.getCurrentHeadset()); + return state == BluetoothHeadset.STATE_DISCONNECTED; + } + + private void checkIsBluetoothPlaying() { + boolean isBluetoothPlaying = false; + Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks(); + + for (BluetoothDevice device : connected) { + if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) { + isBluetoothPlaying = true; + break; + } + } + setBluetoothScanMode(isBluetoothPlaying); + } + + private void sendScanResultsAvailableBroadcast() { + if (!ActivityManagerNative.isSystemReady()) return; + + mContext.sendBroadcast(new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); + } + + private void sendRssiChangeBroadcast(final int newRssi) { + if (!ActivityManagerNative.isSystemReady()) return; + + Intent intent = new Intent(WifiManager.RSSI_CHANGED_ACTION); + intent.putExtra(WifiManager.EXTRA_NEW_RSSI, newRssi); + mContext.sendBroadcast(intent); + } + + private void sendNetworkStateChangeBroadcast(String bssid) { + if (!ActivityManagerNative.isSystemReady()) return; + + Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo); + if (bssid != null) + intent.putExtra(WifiManager.EXTRA_BSSID, bssid); + mContext.sendStickyBroadcast(intent); + } + + private void sendSupplicantStateChangedBroadcast(StateChangeResult sc, boolean failedAuth) { + if (!ActivityManagerNative.isSystemReady()) return; + + Intent intent = new Intent(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT + | Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable)sc.state); + if (failedAuth) { + intent.putExtra( + WifiManager.EXTRA_SUPPLICANT_ERROR, + WifiManager.ERROR_AUTHENTICATING); + } + mContext.sendStickyBroadcast(intent); + } + + private void sendSupplicantConnectionChangedBroadcast(boolean connected) { + if (!ActivityManagerNative.isSystemReady()) return; + + Intent intent = new Intent(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); + intent.putExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, connected); + mContext.sendBroadcast(intent); + } + + /** + * Record the detailed state of a network, and if it is a + * change from the previous state, send a notification to + * any listeners. + * @param state the new @{code DetailedState} + */ + private void setDetailedState(NetworkInfo.DetailedState state) { + Log.d(TAG, "setDetailed state, old =" + + mNetworkInfo.getDetailedState() + " and new state=" + state); + if (state != mNetworkInfo.getDetailedState()) { + mNetworkInfo.setDetailedState(state, null, null); + Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); + msg.sendToTarget(); + } + } + + private static String removeDoubleQuotes(String string) { + if (string.length() <= 2) return ""; + return string.substring(1, string.length() - 1); + } + + private static String convertToQuotedString(String string) { + return "\"" + string + "\""; + } + + private static String makeString(BitSet set, String[] strings) { + StringBuffer buf = new StringBuffer(); + int nextSetBit = -1; + + /* Make sure all set bits are in [0, strings.length) to avoid + * going out of bounds on strings. (Shouldn't happen, but...) */ + set = set.get(0, strings.length); + + while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { + buf.append(strings[nextSetBit].replace('_', '-')).append(' '); + } + + // remove trailing space + if (set.cardinality() > 0) { + buf.setLength(buf.length() - 1); + } + + return buf.toString(); + } + + private static int lookupString(String string, String[] strings) { + int size = strings.length; + + string = string.replace('-', '_'); + + for (int i = 0; i < size; i++) + if (string.equals(strings[i])) + return i; + + // if we ever get here, we should probably add the + // value to WifiConfiguration to reflect that it's + // supported by the WPA supplicant + Log.w(TAG, "Failed to look-up a string: " + string); + + return -1; + } + + private int addOrUpdateNetworkNative(WifiConfiguration config) { + /* + * If the supplied networkId is -1, we create a new empty + * network configuration. Otherwise, the networkId should + * refer to an existing configuration. + */ + int netId = config.networkId; + boolean newNetwork = netId == -1; + // networkId of -1 means we want to create a new network + + if (newNetwork) { + netId = WifiNative.addNetworkCommand(); + if (netId < 0) { + Log.e(TAG, "Failed to add a network!"); + return -1; + } + } + + setVariables: { + + if (config.SSID != null && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.ssidVarName, + config.SSID)) { + Log.d(TAG, "failed to set SSID: "+config.SSID); + break setVariables; + } + + if (config.BSSID != null && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.bssidVarName, + config.BSSID)) { + Log.d(TAG, "failed to set BSSID: "+config.BSSID); + break setVariables; + } + + String allowedKeyManagementString = + makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); + if (config.allowedKeyManagement.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.KeyMgmt.varName, + allowedKeyManagementString)) { + Log.d(TAG, "failed to set key_mgmt: "+ + allowedKeyManagementString); + break setVariables; + } + + String allowedProtocolsString = + makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); + if (config.allowedProtocols.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.Protocol.varName, + allowedProtocolsString)) { + Log.d(TAG, "failed to set proto: "+ + allowedProtocolsString); + break setVariables; + } + + String allowedAuthAlgorithmsString = + makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); + if (config.allowedAuthAlgorithms.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.AuthAlgorithm.varName, + allowedAuthAlgorithmsString)) { + Log.d(TAG, "failed to set auth_alg: "+ + allowedAuthAlgorithmsString); + break setVariables; + } + + String allowedPairwiseCiphersString = + makeString(config.allowedPairwiseCiphers, + WifiConfiguration.PairwiseCipher.strings); + if (config.allowedPairwiseCiphers.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.PairwiseCipher.varName, + allowedPairwiseCiphersString)) { + Log.d(TAG, "failed to set pairwise: "+ + allowedPairwiseCiphersString); + break setVariables; + } + + String allowedGroupCiphersString = + makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); + if (config.allowedGroupCiphers.cardinality() != 0 && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.GroupCipher.varName, + allowedGroupCiphersString)) { + Log.d(TAG, "failed to set group: "+ + allowedGroupCiphersString); + break setVariables; + } + + // Prevent client screw-up by passing in a WifiConfiguration we gave it + // by preventing "*" as a key. + if (config.preSharedKey != null && !config.preSharedKey.equals("*") && + !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.pskVarName, + config.preSharedKey)) { + Log.d(TAG, "failed to set psk: "+config.preSharedKey); + break setVariables; + } + + boolean hasSetKey = false; + if (config.wepKeys != null) { + for (int i = 0; i < config.wepKeys.length; i++) { + // Prevent client screw-up by passing in a WifiConfiguration we gave it + // by preventing "*" as a key. + if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { + if (!WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.wepKeyVarNames[i], + config.wepKeys[i])) { + Log.d(TAG, + "failed to set wep_key"+i+": " + + config.wepKeys[i]); + break setVariables; + } + hasSetKey = true; + } + } + } + + if (hasSetKey) { + if (!WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.wepTxKeyIdxVarName, + Integer.toString(config.wepTxKeyIndex))) { + Log.d(TAG, + "failed to set wep_tx_keyidx: "+ + config.wepTxKeyIndex); + break setVariables; + } + } + + if (!WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.priorityVarName, + Integer.toString(config.priority))) { + Log.d(TAG, config.SSID + ": failed to set priority: " + +config.priority); + break setVariables; + } + + if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( + netId, + WifiConfiguration.hiddenSSIDVarName, + Integer.toString(config.hiddenSSID ? 1 : 0))) { + Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ + config.hiddenSSID); + break setVariables; + } + + for (WifiConfiguration.EnterpriseField field + : config.enterpriseFields) { + String varName = field.varName(); + String value = field.value(); + if (value != null) { + if (field != config.eap) { + value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); + } + if (!WifiNative.setNetworkVariableCommand( + netId, + varName, + value)) { + Log.d(TAG, config.SSID + ": failed to set " + varName + + ": " + value); + break setVariables; + } + } + } + return netId; + } + + if (newNetwork) { + WifiNative.removeNetworkCommand(netId); + Log.d(TAG, + "Failed to set a network variable, removed network: " + + netId); + } + + return -1; + } + + private List<WifiConfiguration> getConfiguredNetworksNative() { + String listStr = WifiNative.listNetworksCommand(); + + List<WifiConfiguration> networks = + new ArrayList<WifiConfiguration>(); + if (listStr == null) + return networks; + + String[] lines = listStr.split("\n"); + // Skip the first line, which is a header + for (int i = 1; i < lines.length; i++) { + String[] result = lines[i].split("\t"); + // network-id | ssid | bssid | flags + WifiConfiguration config = new WifiConfiguration(); + try { + config.networkId = Integer.parseInt(result[0]); + } catch(NumberFormatException e) { + continue; + } + if (result.length > 3) { + if (result[3].indexOf("[CURRENT]") != -1) + config.status = WifiConfiguration.Status.CURRENT; + else if (result[3].indexOf("[DISABLED]") != -1) + config.status = WifiConfiguration.Status.DISABLED; + else + config.status = WifiConfiguration.Status.ENABLED; + } else { + config.status = WifiConfiguration.Status.ENABLED; + } + readNetworkVariables(config); + networks.add(config); + } + return networks; + } + + /** + * Read the variables from the supplicant daemon that are needed to + * fill in the WifiConfiguration object. + * + * @param config the {@link WifiConfiguration} object to be filled in. + */ + private void readNetworkVariables(WifiConfiguration config) { + + int netId = config.networkId; + if (netId < 0) + return; + + /* + * TODO: maybe should have a native method that takes an array of + * variable names and returns an array of values. But we'd still + * be doing a round trip to the supplicant daemon for each variable. + */ + String value; + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); + if (!TextUtils.isEmpty(value)) { + config.SSID = removeDoubleQuotes(value); + } else { + config.SSID = null; + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); + if (!TextUtils.isEmpty(value)) { + config.BSSID = value; + } else { + config.BSSID = null; + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); + config.priority = -1; + if (!TextUtils.isEmpty(value)) { + try { + config.priority = Integer.parseInt(value); + } catch (NumberFormatException ignore) { + } + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); + config.hiddenSSID = false; + if (!TextUtils.isEmpty(value)) { + try { + config.hiddenSSID = Integer.parseInt(value) != 0; + } catch (NumberFormatException ignore) { + } + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); + config.wepTxKeyIndex = -1; + if (!TextUtils.isEmpty(value)) { + try { + config.wepTxKeyIndex = Integer.parseInt(value); + } catch (NumberFormatException ignore) { + } + } + + for (int i = 0; i < 4; i++) { + value = WifiNative.getNetworkVariableCommand(netId, + WifiConfiguration.wepKeyVarNames[i]); + if (!TextUtils.isEmpty(value)) { + config.wepKeys[i] = value; + } else { + config.wepKeys[i] = null; + } + } + + value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); + if (!TextUtils.isEmpty(value)) { + config.preSharedKey = value; + } else { + config.preSharedKey = null; + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.Protocol.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.Protocol.strings); + if (0 <= index) { + config.allowedProtocols.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.KeyMgmt.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.KeyMgmt.strings); + if (0 <= index) { + config.allowedKeyManagement.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.AuthAlgorithm.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.AuthAlgorithm.strings); + if (0 <= index) { + config.allowedAuthAlgorithms.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.PairwiseCipher.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.PairwiseCipher.strings); + if (0 <= index) { + config.allowedPairwiseCiphers.set(index); + } + } + } + + value = WifiNative.getNetworkVariableCommand(config.networkId, + WifiConfiguration.GroupCipher.varName); + if (!TextUtils.isEmpty(value)) { + String vals[] = value.split(" "); + for (String val : vals) { + int index = + lookupString(val, WifiConfiguration.GroupCipher.strings); + if (0 <= index) { + config.allowedGroupCiphers.set(index); + } + } + } + + for (WifiConfiguration.EnterpriseField field : + config.enterpriseFields) { + value = WifiNative.getNetworkVariableCommand(netId, + field.varName()); + if (!TextUtils.isEmpty(value)) { + if (field != config.eap) value = removeDoubleQuotes(value); + field.setValue(value); + } + } + + } + + /** + * Poll for info not reported via events + * RSSI & Linkspeed + */ + private void requestPolledInfo() { + int newRssi = WifiNative.getRssiCommand(); + if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values + /* some implementations avoid negative values by adding 256 + * so we need to adjust for that here. + */ + if (newRssi > 0) newRssi -= 256; + mWifiInfo.setRssi(newRssi); + /* + * Rather then sending the raw RSSI out every time it + * changes, we precalculate the signal level that would + * be displayed in the status bar, and only send the + * broadcast if that much more coarse-grained number + * changes. This cuts down greatly on the number of + * broadcasts, at the cost of not mWifiInforming others + * interested in RSSI of all the changes in signal + * level. + */ + // TODO: The second arg to the call below needs to be a symbol somewhere, but + // it's actually the size of an array of icons that's private + // to StatusBar Policy. + int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4); + if (newSignalLevel != mLastSignalLevel) { + sendRssiChangeBroadcast(newRssi); + } + mLastSignalLevel = newSignalLevel; + } else { + mWifiInfo.setRssi(-200); + } + int newLinkSpeed = WifiNative.getLinkSpeedCommand(); + if (newLinkSpeed != -1) { + mWifiInfo.setLinkSpeed(newLinkSpeed); + } + } + + /** + * Resets the Wi-Fi Connections by clearing any state, resetting any sockets + * using the interface, stopping DHCP & disabling interface + */ + private void handleNetworkDisconnect() { + Log.d(TAG, "Reset connections and stopping DHCP"); + + /* + * Reset connections & stop DHCP + */ + NetworkUtils.resetConnections(mInterfaceName); + + if (!NetworkUtils.stopDhcp(mInterfaceName)) { + Log.e(TAG, "Could not stop DHCP"); + } + + /* Disable interface */ + NetworkUtils.disableInterface(mInterfaceName); + + /* send event to CM & network change broadcast */ + setDetailedState(DetailedState.DISCONNECTED); + sendNetworkStateChangeBroadcast(mLastBssid); + + /* Reset data structures */ + mWifiInfo.setIpAddress(0); + mWifiInfo.setBSSID(null); + mWifiInfo.setSSID(null); + mWifiInfo.setNetworkId(-1); + + /* Clear network properties */ + mNetworkProperties.clear(); + + mLastBssid= null; + mLastNetworkId = -1; + + } + + + /********************************************************* + * Notifications from WifiMonitor + ********************************************************/ + + /** + * A structure for supplying information about a supplicant state + * change in the STATE_CHANGE event message that comes from the + * WifiMonitor + * thread. + */ + private static class StateChangeResult { + StateChangeResult(int networkId, String BSSID, Object state) { + this.state = state; + this.BSSID = BSSID; + this.networkId = networkId; + } + int networkId; + String BSSID; + Object state; + } + + /** + * Send the tracker a notification that a user-entered password key + * may be incorrect (i.e., caused authentication to fail). + */ + void notifyPasswordKeyMayBeIncorrect() { + sendMessage(PASSWORD_MAY_BE_INCORRECT_EVENT); + } + + /** + * Send the tracker a notification that a connection to the supplicant + * daemon has been established. + */ + void notifySupplicantConnection() { + sendMessage(SUP_CONNECTION_EVENT); + } + + /** + * Send the tracker a notification that a connection to the supplicant + * daemon has been established. + */ + void notifySupplicantLost() { + sendMessage(SUP_DISCONNECTION_EVENT); + } + + /** + * Send the tracker a notification that the state of Wifi connectivity + * has changed. + * @param networkId the configured network on which the state change occurred + * @param newState the new network state + * @param BSSID when the new state is {@link DetailedState#CONNECTED + * NetworkInfo.DetailedState.CONNECTED}, + * this is the MAC address of the access point. Otherwise, it + * is {@code null}. + */ + void notifyNetworkStateChange(DetailedState newState, String BSSID, int networkId) { + if (newState == NetworkInfo.DetailedState.CONNECTED) { + sendMessage(obtainMessage(NETWORK_CONNECTION_EVENT, + new StateChangeResult(networkId, BSSID, newState))); + } else { + sendMessage(obtainMessage(NETWORK_DISCONNECTION_EVENT, + new StateChangeResult(networkId, BSSID, newState))); + } + } + + /** + * Send the tracker a notification that the state of the supplicant + * has changed. + * @param networkId the configured network on which the state change occurred + * @param newState the new {@code SupplicantState} + */ + void notifySupplicantStateChange(int networkId, String BSSID, SupplicantState newState) { + sendMessage(obtainMessage(SUPPLICANT_STATE_CHANGE_EVENT, + new StateChangeResult(networkId, BSSID, newState))); + } + + /** + * Send the tracker a notification that a scan has completed, and results + * are available. + */ + void notifyScanResultsAvailable() { + /** + * Switch scan mode over to passive. + * Turning off scan-only mode happens only in "Connect" mode + */ + setScanType(false); + sendMessage(SCAN_RESULTS_EVENT); + } + + void notifyDriverStarted() { + sendMessage(DRIVER_START_EVENT); + } + + void notifyDriverStopped() { + sendMessage(DRIVER_STOP_EVENT); + } + + void notifyDriverHung() { + setWifiEnabled(false); + setWifiEnabled(true); + } + + + /******************************************************** + * HSM states + *******************************************************/ + + class DefaultState extends HierarchicalState { + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch (message.what) { + /* Synchronous call returns */ + case CMD_PING_SUPPLICANT: + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_RECONNECT: + case CMD_REASSOCIATE: + case CMD_REMOVE_NETWORK: + case CMD_ENABLE_NETWORK: + case CMD_DISABLE_NETWORK: + case CMD_ADD_OR_UPDATE_NETWORK: + case CMD_GET_RSSI: + case CMD_GET_RSSI_APPROX: + case CMD_GET_LINK_SPEED: + case CMD_GET_MAC_ADDR: + case CMD_SAVE_CONFIG: + case CMD_CONNECTION_STATUS: + case CMD_GET_NETWORK_CONFIG: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = false; + syncParams.mSyncReturn.intValue = -1; + syncParams.mSyncReturn.stringValue = null; + syncParams.mSyncReturn.configList = null; + notifyOnMsgObject(message); + } + break; + case CM_CMD_TEARDOWN: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + mTeardownRequested.set(true); + sendMessage(CMD_DISCONNECT); + sendMessage(CMD_STOP_DRIVER); + /* Mark wifi available when CM tears down */ + mNetworkInfo.setIsAvailable(true); + break; + case CM_CMD_RECONNECT: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + mTeardownRequested.set(false); + sendMessage(CMD_START_DRIVER); + sendMessage(CMD_RECONNECT); + break; + case CMD_ENABLE_RSSI_POLL: + mEnableRssiPolling = (message.arg1 == 1); + mSupplicantStateTracker.sendMessage(CMD_ENABLE_RSSI_POLL); + break; + default: + if (DBG) Log.w(TAG, "Unhandled " + message); + break; + } + return HANDLED; + } + } + + class InitialState extends HierarchicalState { + @Override + //TODO: could move logging into a common class + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + // [31-8] Reserved for future use + // [7 - 0] HSM state change + // 50021 wifi_state_changed (custom|1|5) + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + if (WifiNative.isDriverLoaded()) { + transitionTo(mDriverLoadedState); + } + else { + transitionTo(mDriverUnloadedState); + } + } + } + + class DriverLoadingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + final Message message = new Message(); + message.copyFrom(getCurrentMessage()); + new Thread(new Runnable() { + public void run() { + sWakeLock.acquire(); + //enabling state + switch(message.arg1) { + case WIFI_STATE_ENABLING: + setWifiState(WIFI_STATE_ENABLING); + break; + case WIFI_AP_STATE_ENABLING: + setWifiApState(WIFI_AP_STATE_ENABLING); + break; + } + + if(WifiNative.loadDriver()) { + Log.d(TAG, "Driver load successful"); + sendMessage(CMD_LOAD_DRIVER_SUCCESS); + } else { + Log.e(TAG, "Failed to load driver!"); + switch(message.arg1) { + case WIFI_STATE_ENABLING: + setWifiState(WIFI_STATE_UNKNOWN); + break; + case WIFI_AP_STATE_ENABLING: + setWifiApState(WIFI_AP_STATE_FAILED); + break; + } + sendMessage(CMD_LOAD_DRIVER_FAILURE); + } + sWakeLock.release(); + } + }).start(); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_LOAD_DRIVER_SUCCESS: + transitionTo(mDriverLoadedState); + break; + case CMD_LOAD_DRIVER_FAILURE: + transitionTo(mDriverFailedState); + break; + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + case CMD_START_SUPPLICANT: + case CMD_STOP_SUPPLICANT: + case CMD_START_AP: + case CMD_STOP_AP: + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverLoadedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch(message.what) { + case CMD_UNLOAD_DRIVER: + transitionTo(mDriverUnloadingState); + break; + case CMD_START_SUPPLICANT: + if(WifiNative.startSupplicant()) { + Log.d(TAG, "Supplicant start successful"); + mWifiMonitor.startMonitoring(); + setWifiState(WIFI_STATE_ENABLED); + transitionTo(mWaitForSupState); + } else { + Log.e(TAG, "Failed to start supplicant!"); + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); + } + break; + case CMD_START_AP: + try { + nwService.startAccessPoint((WifiConfiguration) message.obj, + mInterfaceName, + SOFTAP_IFACE); + } catch(Exception e) { + Log.e(TAG, "Exception in startAccessPoint()"); + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); + break; + } + Log.d(TAG, "Soft AP start successful"); + setWifiApState(WIFI_AP_STATE_ENABLED); + transitionTo(mSoftApStartedState); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverUnloadingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + final Message message = new Message(); + message.copyFrom(getCurrentMessage()); + new Thread(new Runnable() { + public void run() { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + sWakeLock.acquire(); + if(WifiNative.unloadDriver()) { + Log.d(TAG, "Driver unload successful"); + sendMessage(CMD_UNLOAD_DRIVER_SUCCESS); + + switch(message.arg1) { + case WIFI_STATE_DISABLED: + case WIFI_STATE_UNKNOWN: + setWifiState(message.arg1); + break; + case WIFI_AP_STATE_DISABLED: + case WIFI_AP_STATE_FAILED: + setWifiApState(message.arg1); + break; + } + } else { + Log.e(TAG, "Failed to unload driver!"); + sendMessage(CMD_UNLOAD_DRIVER_FAILURE); + + switch(message.arg1) { + case WIFI_STATE_DISABLED: + case WIFI_STATE_UNKNOWN: + setWifiState(WIFI_STATE_UNKNOWN); + break; + case WIFI_AP_STATE_DISABLED: + case WIFI_AP_STATE_FAILED: + setWifiApState(WIFI_AP_STATE_FAILED); + break; + } + } + sWakeLock.release(); + } + }).start(); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_UNLOAD_DRIVER_SUCCESS: + transitionTo(mDriverUnloadedState); + break; + case CMD_UNLOAD_DRIVER_FAILURE: + transitionTo(mDriverFailedState); + break; + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + case CMD_START_SUPPLICANT: + case CMD_STOP_SUPPLICANT: + case CMD_START_AP: + case CMD_STOP_AP: + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverUnloadedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_LOAD_DRIVER: + transitionTo(mDriverLoadingState); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverFailedState extends HierarchicalState { + @Override + public void enter() { + Log.e(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + return NOT_HANDLED; + } + } + + + class WaitForSupState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch(message.what) { + case SUP_CONNECTION_EVENT: + Log.d(TAG, "Supplicant connection established"); + mSupplicantStateTracker.resetSupplicantState(); + /* Initialize data structures */ + resetNotificationTimer(); + setTeardownRequested(false); + mLastBssid = null; + mLastNetworkId = -1; + mLastSignalLevel = -1; + + mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand()); + + //TODO: initialize and fix multicast filtering + //mWM.initializeMulticastFiltering(); + + if (mBluetoothA2dp == null) { + mBluetoothA2dp = new BluetoothA2dp(mContext); + } + checkIsBluetoothPlaying(); + + checkUseStaticIp(); + sendSupplicantConnectionChangedBroadcast(true); + transitionTo(mDriverSupReadyState); + break; + case CMD_STOP_SUPPLICANT: + Log.d(TAG, "Stop supplicant received"); + WifiNative.stopSupplicant(); + transitionTo(mDriverLoadedState); + break; + /* Fail soft ap when waiting for supplicant start */ + case CMD_START_AP: + Log.d(TAG, "Failed to start soft AP with a running supplicant"); + setWifiApState(WIFI_AP_STATE_FAILED); + break; + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + case CMD_STOP_AP: + case CMD_START_SUPPLICANT: + case CMD_UNLOAD_DRIVER: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverSupReadyState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + /* Initialize for connect mode operation at start */ + mIsScanMode = false; + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch(message.what) { + case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */ + Log.d(TAG, "Stop supplicant received"); + WifiNative.stopSupplicant(); + //$FALL-THROUGH$ + case SUP_DISCONNECTION_EVENT: /* Supplicant died */ + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + //Remove any notifications on disconnection + setNotificationVisible(false, 0, false, 0); + WifiNative.closeSupplicantConnection(); + handleNetworkDisconnect(); + sendSupplicantConnectionChangedBroadcast(false); + mSupplicantStateTracker.resetSupplicantState(); + transitionTo(mDriverLoadedState); + + /* When supplicant dies, unload driver and enter failed state */ + //TODO: consider bringing up supplicant again + if (message.what == SUP_DISCONNECTION_EVENT) { + Log.d(TAG, "Supplicant died, unloading driver"); + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0)); + } + break; + case CMD_START_DRIVER: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + WifiNative.startDriverCommand(); + transitionTo(mDriverStartingState); + break; + case SCAN_RESULTS_EVENT: + setScanResults(WifiNative.scanResultsCommand()); + sendScanResultsAvailableBroadcast(); + checkAndSetNotification(); + break; + case CMD_PING_SUPPLICANT: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.pingCommand(); + notifyOnMsgObject(message); + break; + case CMD_ADD_OR_UPDATE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + syncParams = (SyncParams) message.obj; + WifiConfiguration config = (WifiConfiguration) syncParams.mParameter; + syncParams.mSyncReturn.intValue = addOrUpdateNetworkNative(config); + notifyOnMsgObject(message); + break; + case CMD_REMOVE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.removeNetworkCommand( + message.arg1); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.removeNetworkCommand(message.arg1); + } + break; + case CMD_ENABLE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + EnableNetParams enableNetParams = (EnableNetParams) syncParams.mParameter; + syncParams.mSyncReturn.boolValue = WifiNative.enableNetworkCommand( + enableNetParams.netId, enableNetParams.disableOthers); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.enableNetworkCommand(message.arg1, message.arg2 == 1); + } + break; + case CMD_DISABLE_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disableNetworkCommand( + message.arg1); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disableNetworkCommand(message.arg1); + } + break; + case CMD_BLACKLIST_NETWORK: + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + WifiNative.addToBlacklistCommand((String)message.obj); + break; + case CMD_CLEAR_BLACKLIST: + WifiNative.clearBlacklistCommand(); + break; + case CMD_GET_NETWORK_CONFIG: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.configList = getConfiguredNetworksNative(); + notifyOnMsgObject(message); + break; + case CMD_SAVE_CONFIG: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.saveConfigCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.saveConfigCommand(); + } + // Inform the backup manager about a data change + IBackupManager ibm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + if (ibm != null) { + try { + ibm.dataChanged("com.android.providers.settings"); + } catch (Exception e) { + // Try again later + } + } + break; + case CMD_CONNECTION_STATUS: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.stringValue = WifiNative.statusCommand(); + notifyOnMsgObject(message); + break; + case CMD_GET_MAC_ADDR: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.stringValue = WifiNative.getMacAddressCommand(); + notifyOnMsgObject(message); + break; + /* Cannot start soft AP while in client mode */ + case CMD_START_AP: + Log.d(TAG, "Failed to start soft AP with a running supplicant"); + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + setWifiApState(WIFI_AP_STATE_FAILED); + break; + case CMD_SET_SCAN_MODE: + mIsScanMode = (message.arg1 == SCAN_ONLY_MODE); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class DriverStartingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch(message.what) { + case DRIVER_START_EVENT: + transitionTo(mDriverStartedState); + break; + /* Queue driver commands & connection events */ + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case SUPPLICANT_STATE_CHANGE_EVENT: + case NETWORK_CONNECTION_EVENT: + case NETWORK_DISCONNECTION_EVENT: + case PASSWORD_MAY_BE_INCORRECT_EVENT: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + /* Queue the asynchronous version of these commands */ + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_REASSOCIATE: + case CMD_RECONNECT: + if (message.arg2 != SYNCHRONOUS_CALL) { + deferMessage(message); + } + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverStartedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + try { + mBatteryStats.noteWifiRunning(); + } catch (RemoteException ignore) {} + + /* Initialize channel count */ + setNumAllowedChannels(); + + if (mIsScanMode) { + WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); + WifiNative.disconnectCommand(); + transitionTo(mScanModeState); + } else { + WifiNative.setScanResultHandlingCommand(CONNECT_MODE); + WifiNative.reconnectCommand(); + transitionTo(mConnectModeState); + } + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch(message.what) { + case CMD_SET_SCAN_TYPE: + if (message.arg1 == SCAN_ACTIVE) { + WifiNative.setScanModeCommand(true); + } else { + WifiNative.setScanModeCommand(false); + } + break; + case CMD_SET_POWER_MODE: + WifiNative.setPowerModeCommand(message.arg1); + break; + case CMD_SET_BLUETOOTH_COEXISTENCE: + WifiNative.setBluetoothCoexistenceModeCommand(message.arg1); + break; + case CMD_SET_BLUETOOTH_SCAN_MODE: + WifiNative.setBluetoothCoexistenceScanModeCommand(message.arg1 == 1); + break; + case CMD_SET_NUM_ALLOWED_CHANNELS: + mNumAllowedChannels = message.arg1; + WifiNative.setNumAllowedChannelsCommand(message.arg1); + break; + case CMD_START_DRIVER: + /* Ignore another driver start */ + break; + case CMD_STOP_DRIVER: + WifiNative.stopDriverCommand(); + transitionTo(mDriverStoppingState); + break; + case CMD_REQUEST_CM_WAKELOCK: + if (mCm == null) { + mCm = (ConnectivityManager)mContext.getSystemService( + Context.CONNECTIVITY_SERVICE); + } + mCm.requestNetworkTransitionWakelock(TAG); + break; + case CMD_START_PACKET_FILTERING: + WifiNative.startPacketFiltering(); + break; + case CMD_STOP_PACKET_FILTERING: + WifiNative.stopPacketFiltering(); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + @Override + public void exit() { + if (DBG) Log.d(TAG, getName() + "\n"); + try { + mBatteryStats.noteWifiStopped(); + } catch (RemoteException ignore) { } + } + } + + class DriverStoppingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch(message.what) { + case DRIVER_STOP_EVENT: + transitionTo(mDriverStoppedState); + break; + /* Queue driver commands */ + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_TYPE: + case CMD_SET_POWER_MODE: + case CMD_SET_BLUETOOTH_COEXISTENCE: + case CMD_SET_BLUETOOTH_SCAN_MODE: + case CMD_SET_NUM_ALLOWED_CHANNELS: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + deferMessage(message); + break; + /* Queue the asynchronous version of these commands */ + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_REASSOCIATE: + case CMD_RECONNECT: + if (message.arg2 != SYNCHRONOUS_CALL) { + deferMessage(message); + } + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DriverStoppedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + // Take down any open network notifications on driver stop + setNotificationVisible(false, 0, false, 0); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + return NOT_HANDLED; + } + } + + class ScanModeState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + switch(message.what) { + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + /* Ignore */ + return HANDLED; + } else { + WifiNative.setScanResultHandlingCommand(message.arg1); + WifiNative.reconnectCommand(); + mIsScanMode = false; + transitionTo(mDisconnectedState); + } + break; + case CMD_START_SCAN: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.scanCommand( + message.arg1 == SCAN_ACTIVE); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + } + break; + /* Ignore */ + case CMD_DISCONNECT: + case CMD_RECONNECT: + case CMD_REASSOCIATE: + case SUPPLICANT_STATE_CHANGE_EVENT: + case NETWORK_CONNECTION_EVENT: + case NETWORK_DISCONNECTION_EVENT: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class ConnectModeState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + SyncParams syncParams; + StateChangeResult stateChangeResult; + switch(message.what) { + case PASSWORD_MAY_BE_INCORRECT_EVENT: + mPasswordKeyMayBeIncorrect = true; + break; + case SUPPLICANT_STATE_CHANGE_EVENT: + stateChangeResult = (StateChangeResult) message.obj; + mSupplicantStateTracker.handleEvent(stateChangeResult); + break; + case CMD_START_SCAN: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = true; + notifyOnMsgObject(message); + } + /* We need to set scan type in completed state */ + Message newMsg = obtainMessage(); + newMsg.copyFrom(message); + mSupplicantStateTracker.sendMessage(newMsg); + break; + /* Do a redundant disconnect without transition */ + case CMD_DISCONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disconnectCommand(); + } + break; + case CMD_RECONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.reconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.reconnectCommand(); + } + break; + case CMD_REASSOCIATE: + if (message.arg2 == SYNCHRONOUS_CALL) { + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.reassociateCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.reassociateCommand(); + } + break; + case SCAN_RESULTS_EVENT: + /* Set the scan setting back to "connect" mode */ + WifiNative.setScanResultHandlingCommand(CONNECT_MODE); + /* Handle scan results */ + return NOT_HANDLED; + case NETWORK_CONNECTION_EVENT: + Log.d(TAG,"Network connection established"); + stateChangeResult = (StateChangeResult) message.obj; + + /* Remove any notifications */ + setNotificationVisible(false, 0, false, 0); + resetNotificationTimer(); + + mWifiInfo.setBSSID(mLastBssid = stateChangeResult.BSSID); + mWifiInfo.setNetworkId(stateChangeResult.networkId); + mLastNetworkId = stateChangeResult.networkId; + + /* send event to CM & network change broadcast */ + setDetailedState(DetailedState.OBTAINING_IPADDR); + sendNetworkStateChangeBroadcast(mLastBssid); + + transitionTo(mConnectingState); + break; + case NETWORK_DISCONNECTION_EVENT: + Log.d(TAG,"Network connection lost"); + handleNetworkDisconnect(); + transitionTo(mDisconnectedState); + break; + case CMD_GET_RSSI: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.intValue = WifiNative.getRssiCommand(); + notifyOnMsgObject(message); + break; + case CMD_GET_RSSI_APPROX: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.intValue = WifiNative.getRssiApproxCommand(); + notifyOnMsgObject(message); + break; + case CMD_GET_LINK_SPEED: + syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.intValue = WifiNative.getLinkSpeedCommand(); + notifyOnMsgObject(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class ConnectingState extends HierarchicalState { + boolean modifiedBluetoothCoexistenceMode; + int powerMode; + Thread mDhcpThread; + + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + if (!mUseStaticIp) { + + mDhcpThread = null; + modifiedBluetoothCoexistenceMode = false; + powerMode = DRIVER_POWER_MODE_AUTO; + + if (shouldDisableCoexistenceMode()) { + /* + * There are problems setting the Wi-Fi driver's power + * mode to active when bluetooth coexistence mode is + * enabled or sense. + * <p> + * We set Wi-Fi to active mode when + * obtaining an IP address because we've found + * compatibility issues with some routers with low power + * mode. + * <p> + * In order for this active power mode to properly be set, + * we disable coexistence mode until we're done with + * obtaining an IP address. One exception is if we + * are currently connected to a headset, since disabling + * coexistence would interrupt that connection. + */ + modifiedBluetoothCoexistenceMode = true; + + // Disable the coexistence mode + WifiNative.setBluetoothCoexistenceModeCommand( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); + } + + powerMode = WifiNative.getPowerModeCommand(); + if (powerMode < 0) { + // Handle the case where supplicant driver does not support + // getPowerModeCommand. + powerMode = DRIVER_POWER_MODE_AUTO; + } + if (powerMode != DRIVER_POWER_MODE_ACTIVE) { + WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE); + } + + Log.d(TAG, "DHCP request started"); + mDhcpThread = new Thread(new Runnable() { + public void run() { + if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) { + Log.d(TAG, "DHCP request succeeded"); + sendMessage(CMD_IP_CONFIG_SUCCESS); + } else { + Log.d(TAG, "DHCP request failed: " + + NetworkUtils.getDhcpError()); + sendMessage(CMD_IP_CONFIG_FAILURE); + } + } + }); + mDhcpThread.start(); + } else { + if (NetworkUtils.configureInterface(mInterfaceName, mDhcpInfo)) { + Log.v(TAG, "Static IP configuration succeeded"); + sendMessage(CMD_IP_CONFIG_SUCCESS); + } else { + Log.v(TAG, "Static IP configuration failed"); + sendMessage(CMD_IP_CONFIG_FAILURE); + } + } + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + + switch(message.what) { + case CMD_IP_CONFIG_SUCCESS: + mReconnectCount = 0; + mLastSignalLevel = -1; // force update of signal strength + mWifiInfo.setIpAddress(mDhcpInfo.ipAddress); + Log.d(TAG, "IP configuration: " + mDhcpInfo); + configureNetworkProperties(); + setDetailedState(DetailedState.CONNECTED); + sendNetworkStateChangeBroadcast(mLastBssid); + transitionTo(mConnectedState); + break; + case CMD_IP_CONFIG_FAILURE: + mWifiInfo.setIpAddress(0); + + Log.e(TAG, "IP configuration failed"); + /** + * If we've exceeded the maximum number of retries for DHCP + * to a given network, disable the network + */ + if (++mReconnectCount > getMaxDhcpRetries()) { + Log.e(TAG, "Failed " + + mReconnectCount + " times, Disabling " + mLastNetworkId); + WifiNative.disableNetworkCommand(mLastNetworkId); + } + + /* DHCP times out after about 30 seconds, we do a + * disconnect and an immediate reconnect to try again + */ + WifiNative.disconnectCommand(); + WifiNative.reconnectCommand(); + transitionTo(mDisconnectingState); + break; + case CMD_DISCONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + SyncParams syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disconnectCommand(); + } + transitionTo(mDisconnectingState); + break; + /* Ignore */ + case NETWORK_CONNECTION_EVENT: + break; + case CMD_STOP_DRIVER: + sendMessage(CMD_DISCONNECT); + deferMessage(message); + break; + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + sendMessage(CMD_DISCONNECT); + deferMessage(message); + } + break; + case CMD_RECONFIGURE_IP: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + + @Override + public void exit() { + /* reset power state & bluetooth coexistence if on DHCP */ + if (!mUseStaticIp) { + if (powerMode != DRIVER_POWER_MODE_ACTIVE) { + WifiNative.setPowerModeCommand(powerMode); + } + + if (modifiedBluetoothCoexistenceMode) { + // Set the coexistence mode back to its default value + WifiNative.setBluetoothCoexistenceModeCommand( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); + } + } + + } + } + + class ConnectedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_DISCONNECT: + if (message.arg2 == SYNCHRONOUS_CALL) { + SyncParams syncParams = (SyncParams) message.obj; + syncParams.mSyncReturn.boolValue = WifiNative.disconnectCommand(); + notifyOnMsgObject(message); + } else { + /* asynchronous handling */ + WifiNative.disconnectCommand(); + } + transitionTo(mDisconnectingState); + break; + case CMD_RECONFIGURE_IP: + Log.d(TAG,"Reconfiguring IP on connection"); + NetworkUtils.resetConnections(mInterfaceName); + transitionTo(mConnectingState); + break; + case CMD_STOP_DRIVER: + sendMessage(CMD_DISCONNECT); + deferMessage(message); + break; + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + sendMessage(CMD_DISCONNECT); + deferMessage(message); + } + break; + /* Ignore */ + case NETWORK_CONNECTION_EVENT: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DisconnectingState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_STOP_DRIVER: /* Stop driver only after disconnect handled */ + deferMessage(message); + break; + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + deferMessage(message); + } + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class DisconnectedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_SET_SCAN_MODE: + if (message.arg1 == SCAN_ONLY_MODE) { + WifiNative.setScanResultHandlingCommand(message.arg1); + //Supplicant disconnect to prevent further connects + WifiNative.disconnectCommand(); + mIsScanMode = true; + transitionTo(mScanModeState); + } + break; + /* Ignore network disconnect */ + case NETWORK_DISCONNECTION_EVENT: + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class SoftApStartedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch(message.what) { + case CMD_STOP_AP: + Log.d(TAG,"Stopping Soft AP"); + setWifiApState(WIFI_AP_STATE_DISABLING); + try { + nwService.stopAccessPoint(); + } catch(Exception e) { + Log.e(TAG, "Exception in stopAccessPoint()"); + } + transitionTo(mDriverLoadedState); + break; + case CMD_START_AP: + Log.d(TAG,"SoftAP set on a running access point"); + try { + nwService.setAccessPoint((WifiConfiguration) message.obj, + mInterfaceName, + SOFTAP_IFACE); + } catch(Exception e) { + Log.e(TAG, "Exception in nwService during soft AP set"); + try { + nwService.stopAccessPoint(); + } catch (Exception ee) { + Slog.e(TAG, "Could not stop AP, :" + ee); + } + sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); + } + break; + /* Fail client mode operation when soft AP is enabled */ + case CMD_START_SUPPLICANT: + Log.e(TAG,"Cannot start supplicant with a running soft AP"); + setWifiState(WIFI_STATE_UNKNOWN); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + + class SupplicantStateTracker extends HierarchicalStateMachine { + + private int mRssiPollToken = 0; + + /** + * The max number of the WPA supplicant loop iterations before we + * decide that the loop should be terminated: + */ + private static final int MAX_SUPPLICANT_LOOP_ITERATIONS = 4; + private int mLoopDetectIndex = 0; + private int mLoopDetectCount = 0; + + /** + * Supplicant state change commands follow + * the ordinal values defined in SupplicantState.java + */ + private static final int DISCONNECTED = 0; + private static final int INACTIVE = 1; + private static final int SCANNING = 2; + private static final int ASSOCIATING = 3; + private static final int ASSOCIATED = 4; + private static final int FOUR_WAY_HANDSHAKE = 5; + private static final int GROUP_HANDSHAKE = 6; + private static final int COMPLETED = 7; + private static final int DORMANT = 8; + private static final int UNINITIALIZED = 9; + private static final int INVALID = 10; + + private HierarchicalState mUninitializedState; + private HierarchicalState mInitializedState; + private HierarchicalState mInactiveState; + private HierarchicalState mDisconnectState; + private HierarchicalState mScanState; + private HierarchicalState mConnectState; + private HierarchicalState mHandshakeState; + private HierarchicalState mCompletedState; + private HierarchicalState mDormantState; + + + public SupplicantStateTracker(Context context, Handler target) { + super(TAG, target.getLooper()); + + mUninitializedState = new UninitializedState(); + mInitializedState = new InitializedState(); + mInactiveState = new InactiveState(); + mDisconnectState = new DisconnectedState(); + mScanState = new ScanState(); + mConnectState = new ConnectState(); + mHandshakeState = new HandshakeState(); + mCompletedState = new CompletedState(); + mDormantState = new DormantState(); + + + addState(mUninitializedState); + addState(mInitializedState); + addState(mInactiveState, mInitializedState); + addState(mDisconnectState, mInitializedState); + addState(mScanState, mInitializedState); + addState(mConnectState, mInitializedState); + addState(mHandshakeState, mConnectState); + addState(mCompletedState, mConnectState); + addState(mDormantState, mInitializedState); + + setInitialState(mUninitializedState); + + //start the state machine + start(); + } + + public void handleEvent(StateChangeResult stateChangeResult) { + SupplicantState newState = (SupplicantState) stateChangeResult.state; + + // Supplicant state change + // [31-13] Reserved for future use + // [8 - 0] Supplicant state (as defined in SupplicantState.java) + // 50023 supplicant_state_changed (custom|1|5) + EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, newState.ordinal()); + + sendMessage(obtainMessage(newState.ordinal(), stateChangeResult)); + } + + public void resetSupplicantState() { + transitionTo(mUninitializedState); + } + + private void resetLoopDetection() { + mLoopDetectCount = 0; + mLoopDetectIndex = 0; + } + + private boolean handleTransition(Message msg) { + if (DBG) Log.d(TAG, getName() + msg.toString() + "\n"); + switch (msg.what) { + case DISCONNECTED: + transitionTo(mDisconnectState); + break; + case SCANNING: + transitionTo(mScanState); + break; + case ASSOCIATING: + StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; + /* BSSID is valid only in ASSOCIATING state */ + mWifiInfo.setBSSID(stateChangeResult.BSSID); + //$FALL-THROUGH$ + case ASSOCIATED: + case FOUR_WAY_HANDSHAKE: + case GROUP_HANDSHAKE: + transitionTo(mHandshakeState); + break; + case COMPLETED: + transitionTo(mCompletedState); + break; + case DORMANT: + transitionTo(mDormantState); + break; + case INACTIVE: + transitionTo(mInactiveState); + break; + case UNINITIALIZED: + case INVALID: + transitionTo(mUninitializedState); + break; + default: + return NOT_HANDLED; + } + StateChangeResult stateChangeResult = (StateChangeResult) msg.obj; + SupplicantState supState = (SupplicantState) stateChangeResult.state; + setDetailedState(WifiInfo.getDetailedStateOf(supState)); + mWifiInfo.setSupplicantState(supState); + mWifiInfo.setNetworkId(stateChangeResult.networkId); + //TODO: Modify WifiMonitor to report SSID on events + //mWifiInfo.setSSID() + return HANDLED; + } + + /******************************************************** + * HSM states + *******************************************************/ + + class InitializedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + } + @Override + public boolean processMessage(Message message) { + if (DBG) Log.d(TAG, getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_START_SCAN: + WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + break; + default: + if (DBG) Log.w(TAG, "Ignoring " + message); + break; + } + return HANDLED; + } + } + + class UninitializedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + mNetworkInfo.setIsAvailable(false); + resetLoopDetection(); + mPasswordKeyMayBeIncorrect = false; + } + @Override + public boolean processMessage(Message message) { + switch(message.what) { + default: + if (!handleTransition(message)) { + if (DBG) Log.w(TAG, "Ignoring " + message); + } + break; + } + return HANDLED; + } + } + + class InactiveState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(false); + resetLoopDetection(); + mPasswordKeyMayBeIncorrect = false; + + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + + class DisconnectedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(true); + resetLoopDetection(); + + /* If a disconnect event happens after a password key failure + * event, disable the network + */ + if (mPasswordKeyMayBeIncorrect) { + Log.d(TAG, "Failed to authenticate, disabling network " + + mWifiInfo.getNetworkId()); + WifiNative.disableNetworkCommand(mWifiInfo.getNetworkId()); + mPasswordKeyMayBeIncorrect = false; + sendSupplicantStateChangedBroadcast(stateChangeResult, true); + } + else { + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + class ScanState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(true); + mPasswordKeyMayBeIncorrect = false; + resetLoopDetection(); + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + class ConnectState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + } + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_START_SCAN: + WifiNative.setScanResultHandlingCommand(SCAN_ONLY_MODE); + WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + class HandshakeState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + final Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(true); + + if (mLoopDetectIndex > message.what) { + mLoopDetectCount++; + } + if (mLoopDetectCount > MAX_SUPPLICANT_LOOP_ITERATIONS) { + WifiNative.disableNetworkCommand(stateChangeResult.networkId); + mLoopDetectCount = 0; + } + + mLoopDetectIndex = message.what; + + mPasswordKeyMayBeIncorrect = false; + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + + class CompletedState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(true); + + mRssiPollToken++; + if (mEnableRssiPolling) { + sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), + POLL_RSSI_INTERVAL_MSECS); + } + + resetLoopDetection(); + + mPasswordKeyMayBeIncorrect = false; + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + } + @Override + public boolean processMessage(Message message) { + switch(message.what) { + case ASSOCIATING: + case ASSOCIATED: + case FOUR_WAY_HANDSHAKE: + case GROUP_HANDSHAKE: + case COMPLETED: + break; + case CMD_RSSI_POLL: + if (message.arg1 == mRssiPollToken) { + // Get Info and continue polling + requestPolledInfo(); + sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), + POLL_RSSI_INTERVAL_MSECS); + } else { + // Polling has completed + } + break; + case CMD_ENABLE_RSSI_POLL: + mRssiPollToken++; + if (mEnableRssiPolling) { + // first poll + requestPolledInfo(); + sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), + POLL_RSSI_INTERVAL_MSECS); + } + break; + default: + return handleTransition(message); + } + return HANDLED; + } + } + + class DormantState extends HierarchicalState { + @Override + public void enter() { + if (DBG) Log.d(TAG, getName() + "\n"); + Message message = getCurrentMessage(); + StateChangeResult stateChangeResult = (StateChangeResult) message.obj; + + mNetworkInfo.setIsAvailable(true); + resetLoopDetection(); + mPasswordKeyMayBeIncorrect = false; + + sendSupplicantStateChangedBroadcast(stateChangeResult, false); + + /* TODO: reconnect is now being handled at DHCP failure handling + * If we run into issues with staying in Dormant state, might + * need a reconnect here + */ + } + @Override + public boolean processMessage(Message message) { + return handleTransition(message); + } + } + } + private class NotificationEnabledSettingObserver extends ContentObserver { public NotificationEnabledSettingObserver(Handler handler) { @@ -2627,8 +3971,4 @@ public class WifiStateTracker extends Handler implements NetworkStateTracker { Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; } } - - public NetworkProperties getNetworkProperties() { - return mNetworkProperties; - } } |