diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/accounts/AccountManager.java | 142 | ||||
-rw-r--r-- | core/java/android/app/ListActivity.java | 2 | ||||
-rw-r--r-- | core/java/android/content/ContentProvider.java | 179 | ||||
-rw-r--r-- | core/java/android/content/SyncOperation.java | 2 | ||||
-rw-r--r-- | core/java/android/content/res/ColorStateList.java | 4 | ||||
-rw-r--r-- | core/java/android/database/sqlite/SQLiteOpenHelper.java | 39 | ||||
-rw-r--r-- | core/java/android/os/IPowerManager.aidl | 1 | ||||
-rw-r--r-- | core/java/android/widget/BaseExpandableListAdapter.java | 4 | ||||
-rw-r--r-- | core/java/android/widget/HeterogeneousExpandableList.java | 27 | ||||
-rw-r--r-- | core/java/android/widget/Scroller.java | 6 | ||||
-rw-r--r-- | core/tests/coretests/AndroidManifest.xml | 2 | ||||
-rw-r--r-- | core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java | 440 |
12 files changed, 689 insertions, 159 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index b0adaec..fd3a0d0 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -45,26 +45,26 @@ import com.google.android.collect.Maps; /** * This class provides access to a centralized registry of the user's - * online accounts. With this service, users only need to enter their - * credentials (username and password) once for any account, granting - * applications access to online resources with "one-click" approval. + * online accounts. The user enters credentials (username and password) once + * per account, granting applications access to online resources with + * "one-click" approval. * * <p>Different online services have different ways of handling accounts and * authentication, so the account manager uses pluggable <em>authenticator</em> - * modules for different <em>account types</em>. The authenticators (which - * may be written by third parties) handle the actual details of validating - * account credentials and storing account information. For example, Google, - * Facebook, and Microsoft Exchange each have their own authenticator. + * modules for different <em>account types</em>. Authenticators (which may be + * written by third parties) handle the actual details of validating account + * credentials and storing account information. For example, Google, Facebook, + * and Microsoft Exchange each have their own authenticator. * * <p>Many servers support some notion of an <em>authentication token</em>, * which can be used to authenticate a request to the server without sending * the user's actual password. (Auth tokens are normally created with a * separate request which does include the user's credentials.) AccountManager - * can generate these auth tokens for applications, so the application doesn't - * need to handle passwords directly. Auth tokens are normally reusable, and - * cached by AccountManager, but must be refreshed periodically. It's the - * responsibility of applications to <em>invalidate</em> auth tokens when they - * stop working so the AccountManager knows it needs to regenerate them. + * can generate auth tokens for applications, so the application doesn't need to + * handle passwords directly. Auth tokens are normally reusable and cached by + * AccountManager, but must be refreshed periodically. It's the responsibility + * of applications to <em>invalidate</em> auth tokens when they stop working so + * the AccountManager knows it needs to regenerate them. * * <p>Applications accessing a server normally go through these steps: * @@ -84,14 +84,19 @@ import com.google.android.collect.Maps; * {@link #addAccount} may be called to prompt the user to create an * account of the appropriate type. * + * <li><b>Important:</b> If the application is using a previously remembered + * account selection, it must make sure the account is still in the list + * of accounts returned by {@link #getAccountsByType}. Requesting an auth token + * for an account no longer on the device results in an undefined failure. + * * <li>Request an auth token for the selected account(s) using one of the * {@link #getAuthToken} methods or related helpers. Refer to the description * of each method for exact usage and error handling details. * * <li>Make the request using the auth token. The form of the auth token, * the format of the request, and the protocol used are all specific to the - * service you are accessing. The application makes the request itself, using - * whatever network and protocol libraries are useful. + * service you are accessing. The application may use whatever network and + * protocol libraries are useful. * * <li><b>Important:</b> If the request fails with an authentication error, * it could be that a cached auth token is stale and no longer honored by @@ -103,7 +108,7 @@ import com.google.android.collect.Maps; * appropriate actions taken. * </ul> * - * <p>Some AccountManager methods may require interaction with the user to + * <p>Some AccountManager methods may need to interact with the user to * prompt for credentials, present options, or ask the user to add an account. * The caller may choose whether to allow AccountManager to directly launch the * necessary user interface and wait for the user, or to return an Intent which @@ -113,18 +118,17 @@ import com.google.android.collect.Maps; * the current foreground {@link Activity} context. * * <p>Many AccountManager methods take {@link AccountManagerCallback} and - * {@link Handler} as parameters. These methods return immediately but + * {@link Handler} as parameters. These methods return immediately and * run asynchronously. If a callback is provided then * {@link AccountManagerCallback#run} will be invoked on the Handler's * thread when the request completes, successfully or not. - * An {@link AccountManagerFuture} is returned by these requests and also - * supplied to the callback (if any). The result is retrieved by calling - * {@link AccountManagerFuture#getResult()} which waits for the operation - * to complete (if necessary) and either returns the result or throws an - * exception if an error occurred during the operation. - * To make the request synchronously, call + * The result is retrieved by calling {@link AccountManagerFuture#getResult()} + * on the {@link AccountManagerFuture} returned by the method (and also passed + * to the callback). This method waits for the operation to complete (if + * necessary) and either returns the result or throws an exception if an error + * occurred during the operation. To make the request synchronously, call * {@link AccountManagerFuture#getResult()} immediately on receiving the - * future from the method. No callback need be supplied. + * future from the method; no callback need be supplied. * * <p>Requests which may block, including * {@link AccountManagerFuture#getResult()}, must never be called on @@ -143,32 +147,32 @@ public class AccountManager { public static final int ERROR_CODE_BAD_REQUEST = 8; /** - * The Bundle key used for the {@link String} account name in results + * Bundle key used for the {@link String} account name in results * from methods which return information about a particular account. */ public static final String KEY_ACCOUNT_NAME = "authAccount"; /** - * The Bundle key used for the {@link String} account type in results + * Bundle key used for the {@link String} account type in results * from methods which return information about a particular account. */ public static final String KEY_ACCOUNT_TYPE = "accountType"; /** - * The Bundle key used for the auth token value in results + * Bundle key used for the auth token value in results * from {@link #getAuthToken} and friends. */ public static final String KEY_AUTHTOKEN = "authtoken"; /** - * The Bundle key used for an {@link Intent} in results from methods that + * Bundle key used for an {@link Intent} in results from methods that * may require the caller to interact with the user. The Intent can * be used to start the corresponding user interface activity. */ public static final String KEY_INTENT = "intent"; /** - * The Bundle key used to supply the password directly in options to + * Bundle key used to supply the password directly in options to * {@link #confirmCredentials}, rather than prompting the user with * the standard password prompt. */ @@ -476,7 +480,7 @@ public class AccountManager { * @param account The {@link Account} to add * @param password The password to associate with the account, null for none * @param userdata String values to use for the account's userdata, null for none - * @return Whether the account was successfully added. False if the account + * @return True if the account was successfully added, false if the account * already exists, the account is null, or another error occurs. */ public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { @@ -733,15 +737,14 @@ public class AccountManager { * sense to ask the user directly for a password. * * <p>If a previously generated auth token is cached for this account and - * type, then it will be returned. Otherwise, if we have a saved password - * the server accepts, it will be used to generate a new auth token. - * Otherwise, the user will be asked for a password, which will be sent to - * the server to generate a new auth token. + * type, then it is returned. Otherwise, if a saved password is + * available, it is sent to the server to generate a new auth token. + * Otherwise, the user is prompted to enter a password. * - * <p>The value of the auth token type depends on the authenticator. - * Some services use different tokens to access different functionality -- - * for example, Google uses different auth tokens to access Gmail and - * Google Calendar for the same account. + * <p>Some authenticators have auth token <em>types</em>, whose value + * is authenticator-dependent. Some services use different token types to + * access different functionality -- for example, Google uses different auth + * tokens to access Gmail and Google Calendar for the same account. * * <p>This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. @@ -778,6 +781,9 @@ public class AccountManager { * <li> {@link IOException} if the authenticator experienced an I/O problem * creating a new auth token, usually because of network trouble * </ul> + * If the account is no longer present on the device, the return value is + * authenticator-dependent. The caller should verify the validity of the + * account before requesting an auth token. */ public AccountManagerFuture<Bundle> getAuthToken( final Account account, final String authTokenType, final Bundle options, @@ -800,29 +806,27 @@ public class AccountManager { * user should not be immediately interrupted with a password prompt. * * <p>If a previously generated auth token is cached for this account and - * type, then it will be returned. Otherwise, if we have saved credentials - * the server accepts, it will be used to generate a new auth token. - * Otherwise, an Intent will be returned which, when started, will prompt - * the user for a password. If the notifyAuthFailure parameter is set, - * the same Intent will be associated with a status bar notification, + * type, then it is returned. Otherwise, if a saved password is + * available, it is sent to the server to generate a new auth token. + * Otherwise, an {@link Intent} is returned which, when started, will + * prompt the user for a password. If the notifyAuthFailure parameter is + * set, a status bar notification is also created with the same Intent, * alerting the user that they need to enter a password at some point. * - * <p>If the intent is left in a notification, you will need to wait until - * the user gets around to entering a password before trying again, - * which could be hours or days or never. When it does happen, the - * account manager will broadcast the {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} - * {@link Intent}, which applications can use to trigger another attempt - * to fetch an auth token. + * <p>In that case, you may need to wait until the user responds, which + * could take hours or days or forever. When the user does respond and + * supply a new password, the account manager will broadcast the + * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can + * use to try again. * - * <p>If notifications are not enabled, it is the application's - * responsibility to launch the returned intent at some point to let - * the user enter credentials. In either case, the result from this - * call will not wait for user action. + * <p>If notifyAuthFailure is not set, it is the application's + * responsibility to launch the returned Intent at some point. + * Either way, the result from this call will not wait for user action. * - * <p>The value of the auth token type depends on the authenticator. - * Some services use different tokens to access different functionality -- - * for example, Google uses different auth tokens to access Gmail and - * Google Calendar for the same account. + * <p>Some authenticators have auth token <em>types</em>, whose value + * is authenticator-dependent. Some services use different token types to + * access different functionality -- for example, Google uses different auth + * tokens to access Gmail and Google Calendar for the same account. * * <p>This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. @@ -851,7 +855,7 @@ public class AccountManager { * must enter credentials, the returned Bundle contains only * {@link #KEY_INTENT} with the {@link Intent} needed to launch a prompt. * - * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * If an error occurred, {@link AccountManagerFuture#getResult()} throws: * <ul> * <li> {@link AuthenticatorException} if the authenticator failed to respond * <li> {@link OperationCanceledException} if the operation is canceled for @@ -859,6 +863,9 @@ public class AccountManager { * <li> {@link IOException} if the authenticator experienced an I/O problem * creating a new auth token, usually because of network trouble * </ul> + * If the account is no longer present on the device, the return value is + * authenticator-dependent. The caller should verify the validity of the + * account before requesting an auth token. */ public AccountManagerFuture<Bundle> getAuthToken( final Account account, final String authTokenType, final boolean notifyAuthFailure, @@ -910,9 +917,8 @@ public class AccountManager { * * If no activity was specified, the returned Bundle contains only * {@link #KEY_INTENT} with the {@link Intent} needed to launch the - * actual account creation process. - * - * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * actual account creation process. If an error occurred, + * {@link AccountManagerFuture#getResult()} throws: * <ul> * <li> {@link AuthenticatorException} if no authenticator was registered for * this account type or the authenticator failed to respond @@ -979,9 +985,8 @@ public class AccountManager { * * If no activity or password was specified, the returned Bundle contains * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the - * password prompt. - * - * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * password prompt. If an error occurred, + * {@link AccountManagerFuture#getResult()} throws: * <ul> * <li> {@link AuthenticatorException} if the authenticator failed to respond * <li> {@link OperationCanceledException} if the operation was canceled for @@ -1040,9 +1045,8 @@ public class AccountManager { * * If no activity was specified, the returned Bundle contains only * {@link #KEY_INTENT} with the {@link Intent} needed to launch the - * password prompt. - * - * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * password prompt. If an error occurred, + * {@link AccountManagerFuture#getResult()} throws: * <ul> * <li> {@link AuthenticatorException} if the authenticator failed to respond * <li> {@link OperationCanceledException} if the operation was canceled for @@ -1091,8 +1095,8 @@ public class AccountManager { * which is empty if properties were edited successfully, or * if no activity was specified, contains only {@link #KEY_INTENT} * needed to launch the authenticator's settings dialog. - * - * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * If an error occurred, {@link AccountManagerFuture#getResult()} + * throws: * <ul> * <li> {@link AuthenticatorException} if no authenticator was registered for * this account type or the authenticator failed to respond @@ -1617,7 +1621,7 @@ public class AccountManager { * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted * </ul> * - * <p>If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * If an error occurred, {@link AccountManagerFuture#getResult()} throws: * <ul> * <li> {@link AuthenticatorException} if no authenticator was registered for * this account type or the authenticator failed to respond diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java index 84a57b5..0a80207 100644 --- a/core/java/android/app/ListActivity.java +++ b/core/java/android/app/ListActivity.java @@ -68,7 +68,7 @@ import android.widget.ListView; * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * - * <TextView id="@id/android:empty" + * <TextView android:id="@id/android:empty" * android:layout_width="match_parent" * android:layout_height="match_parent" * android:background="#FF0000" diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 5fb2aae..9b9f796 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>If you use SQLite, {@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 handle requests 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 handle requests to open a file blob. + * 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>This method 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 + * file slices, 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,21 @@ 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 handle requests to perform a batch of operations, or the + * default implementation will iterate over the operations and call + * {@link ContentProviderOperation#apply} on each of them. + * If all calls to {@link ContentProviderOperation#apply} succeed + * then a {@link ContentProviderResult} array with as many + * elements as there were operations will be returned. If any of the 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/SyncOperation.java b/core/java/android/content/SyncOperation.java index a7d036f..b016088 100644 --- a/core/java/android/content/SyncOperation.java +++ b/core/java/android/content/SyncOperation.java @@ -90,7 +90,7 @@ public class SyncOperation implements Comparable { private String toKey() { StringBuilder sb = new StringBuilder(); sb.append("authority: ").append(authority); - sb.append(" account: ").append(account); + sb.append(" account {name=" + account.name + ", type=" + account.type + "}"); sb.append(" extras: "); extrasToStringBuilder(extras, sb, true /* asKey */); return sb.toString(); diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 70baaef..bd23db4 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -45,7 +45,6 @@ import java.util.Arrays; * <item android:state_focused="true" android:color="@color/testcolor1"/> * <item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" /> * <item android:state_enabled="false" android:color="@color/testcolor3" /> - * <item android:state_active="true" android:color="@color/testcolor4" /> * <item android:color="@color/testcolor5"/> * </selector> * </pre> @@ -56,6 +55,9 @@ import java.util.Arrays; * An item with no state spec is considered to match any set of states and is generally useful as * a final item to be used as a default. Note that if you have such an item before any other items * in the list then any subsequent items will end up being ignored. + * <p>For more information, see the guide to <a + * href="{@docRoot}guide/topics/resources/color-list-resource.html">Color State + * List Resource</a>.</p> */ public class ColorStateList implements Parcelable { diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 52aac3a..47002b6 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -22,10 +22,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> */ @@ -42,8 +48,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 @@ -62,13 +69,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 */ @@ -141,6 +155,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. @@ -219,9 +238,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/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 29aeb19..90ef1cb 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -26,6 +26,7 @@ interface IPowerManager void releaseWakeLock(IBinder lock, int flags); void userActivity(long when, boolean noChangeLights); void userActivityWithForce(long when, boolean noChangeLights, boolean force); + void clearUserActivityTimeout(long now, long timeout); void setPokeLock(int pokey, IBinder lock, String tag); int getSupportedWakeLockFlags(); void setStayOnSetting(int val); diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java index 396b7ae..b4d6ad7 100644 --- a/core/java/android/widget/BaseExpandableListAdapter.java +++ b/core/java/android/widget/BaseExpandableListAdapter.java @@ -43,14 +43,14 @@ public abstract class BaseExpandableListAdapter implements ExpandableListAdapter } /** - * {@see DataSetObservable#notifyInvalidated()} + * @see DataSetObservable#notifyInvalidated() */ public void notifyDataSetInvalidated() { mDataSetObservable.notifyInvalidated(); } /** - * {@see DataSetObservable#notifyChanged()} + * @see DataSetObservable#notifyChanged() */ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java index 1292733..e7e0933 100644 --- a/core/java/android/widget/HeterogeneousExpandableList.java +++ b/core/java/android/widget/HeterogeneousExpandableList.java @@ -23,12 +23,13 @@ import android.view.ViewGroup; * Additional methods that when implemented make an * {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type * mechanism. - * - * An {@link ExpandableListAdapter} declares one view type for its group items + * <p> + * An {@link ExpandableListAdapter} declares it has one view type for its group items * and one view type for its child items. Although adapted for most {@link ExpandableListView}s, - * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain - * different types of group and/or child item views, should use an adapter that implements this - * interface. This way, the recycled views that will be provided to + * these values should be tuned for heterogeneous {@link ExpandableListView}s. + * </p> + * Lists that contain different types of group and/or child item views, should use an adapter that + * implements this interface. This way, the recycled views that will be provided to * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)} * and * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)} @@ -48,7 +49,7 @@ public interface HeterogeneousExpandableList { * . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1. * {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned. * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE - * @see getGroupTypeCount() + * @see #getGroupTypeCount() */ int getGroupType(int groupPosition); @@ -65,7 +66,7 @@ public interface HeterogeneousExpandableList { * Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1. * {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned. * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE - * @see getChildTypeCount() + * @see #getChildTypeCount() */ int getChildType(int groupPosition, int childPosition); @@ -78,13 +79,11 @@ public interface HeterogeneousExpandableList { * . If the adapter always returns the same type of View for all group items, this method should * return 1. * </p> - * <p> * This method will only be called when the adapter is set on the {@link AdapterView}. - * </p> * * @return The number of types of group Views that will be created by this adapter. - * @see getChildTypeCount() - * @see getGroupType() + * @see #getChildTypeCount() + * @see #getGroupType(int) */ int getGroupTypeCount(); @@ -97,13 +96,11 @@ public interface HeterogeneousExpandableList { * , for any group. If the adapter always returns the same type of View for * all child items, this method should return 1. * </p> - * <p> * This method will only be called when the adapter is set on the {@link AdapterView}. - * </p> * * @return The total number of types of child Views that will be created by this adapter. - * @see getGroupTypeCount() - * @see getChildType() + * @see #getGroupTypeCount() + * @see #getChildType(int, int) */ int getChildTypeCount(); } diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java index 784a75f..4cb0839 100644 --- a/core/java/android/widget/Scroller.java +++ b/core/java/android/widget/Scroller.java @@ -218,7 +218,11 @@ public class Scroller { // Pin to mMinY <= mCurrY <= mMaxY mCurrY = Math.min(mCurrY, mMaxY); mCurrY = Math.max(mCurrY, mMinY); - + + if (mCurrX == mFinalX && mCurrY == mFinalY) { + mFinished = true; + } + break; } } diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index a77717f..036da95 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -36,6 +36,8 @@ android:description="@string/permdesc_testDenied" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.BLUETOOTH" /> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java new file mode 100644 index 0000000..0fe83e1 --- /dev/null +++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java @@ -0,0 +1,440 @@ +/* + * 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.bluetooth; + +import android.app.Instrumentation; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +public class BluetoothStressTest extends InstrumentationTestCase { + private static final String TAG = "BluetoothEnablerStressTest"; + + /** + * Timeout for {@link BluetoothAdapter#disable()} in ms. + */ + private static final int DISABLE_TIMEOUT = 5000; + + /** + * Timeout for {@link BluetoothAdapter#enable()} in ms. + */ + private static final int ENABLE_TIMEOUT = 20000; + + /** + * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms. + */ + private static final int SET_SCAN_MODE_TIMEOUT = 5000; + + /** + * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms. + */ + private static final int START_DISCOVERY_TIMEOUT = 5000; + + /** + * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms. + */ + private static final int CANCEL_DISCOVERY_TIMEOUT = 5000; + + private static final int DISCOVERY_STARTED_FLAG = 1; + private static final int DISCOVERY_FINISHED_FLAG = 1 << 1; + private static final int SCAN_MODE_NONE_FLAG = 1 << 2; + private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3; + private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4; + private static final int STATE_OFF_FLAG = 1 << 5; + private static final int STATE_TURNING_ON_FLAG = 1 << 6; + private static final int STATE_ON_FLAG = 1 << 7; + private static final int STATE_TURNING_OFF_FLAG = 1 << 8; + + /** + * Time between polls in ms. + */ + private static final int POLL_TIME = 100; + + private static final int ENABLE_ITERATIONS = 100; + private static final int DISCOVERABLE_ITERATIONS = 1000; + private static final int SCAN_ITERATIONS = 1000; + + private Context mContext; + + private Instrumentation mInstrumentation; + + private class BluetoothReceiver extends BroadcastReceiver { + private int mFiredFlags = 0; + + @Override + public void onReceive(Context context, Intent intent) { + synchronized (this) { + if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { + mFiredFlags |= DISCOVERY_STARTED_FLAG; + } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { + mFiredFlags |= DISCOVERY_FINISHED_FLAG; + } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) { + int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, + BluetoothAdapter.ERROR); + assertNotSame(mode, BluetoothAdapter.ERROR); + switch (mode) { + case BluetoothAdapter.SCAN_MODE_NONE: + mFiredFlags |= SCAN_MODE_NONE_FLAG; + break; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE: + mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG; + break; + case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: + mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; + break; + } + } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, + BluetoothAdapter.ERROR); + assertNotSame(state, BluetoothAdapter.ERROR); + switch (state) { + case BluetoothAdapter.STATE_OFF: + mFiredFlags |= STATE_OFF_FLAG; + break; + case BluetoothAdapter.STATE_TURNING_ON: + mFiredFlags |= STATE_TURNING_ON_FLAG; + break; + case BluetoothAdapter.STATE_ON: + mFiredFlags |= STATE_ON_FLAG; + break; + case BluetoothAdapter.STATE_TURNING_OFF: + mFiredFlags |= STATE_TURNING_OFF_FLAG; + break; + } + } + } + } + + public int getFiredFlags() { + synchronized (this) { + return mFiredFlags; + } + } + + public void resetFiredFlags() { + synchronized (this) { + mFiredFlags = 0; + } + } + } + + private BluetoothReceiver mReceiver = new BluetoothReceiver(); + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mInstrumentation = getInstrumentation(); + mContext = mInstrumentation.getTargetContext(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); + filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiver(mReceiver, filter); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + mContext.unregisterReceiver(mReceiver); + } + + @LargeTest + public void testEnableDisable() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + + for (int i = 0; i < ENABLE_ITERATIONS; i++) { + Log.i(TAG, "Enable iteration " + (i + 1) + " of " + ENABLE_ITERATIONS); + enable(adapter); + disable(adapter); + } + } + + @LargeTest + public void testDiscoverable() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + enable(adapter); + + for (int i = 0; i < DISCOVERABLE_ITERATIONS; i++) { + Log.i(TAG, "Discoverable iteration " + (i + 1) + " of " + DISCOVERABLE_ITERATIONS); + discoverable(adapter); + undiscoverable(adapter); + } + + disable(adapter); + } + + @LargeTest + public void testScan() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + enable(adapter); + + for (int i = 0; i < SCAN_ITERATIONS; i++) { + Log.i(TAG, "Scan iteration " + (i + 1) + " of " + SCAN_ITERATIONS); + startScan(adapter); + stopScan(adapter); + } + + disable(adapter); + } + + private void disable(BluetoothAdapter adapter) { + int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG; + mReceiver.resetFiredFlags(); + + int state = adapter.getState(); + switch (state) { + case BluetoothAdapter.STATE_OFF: + assertFalse(adapter.isEnabled()); + return; + case BluetoothAdapter.STATE_ON: + assertTrue(adapter.isEnabled()); + assertTrue(adapter.disable()); + break; + case BluetoothAdapter.STATE_TURNING_ON: + assertFalse(adapter.isEnabled()); + assertTrue(adapter.disable()); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + assertFalse(adapter.isEnabled()); + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + fail("disable() invalid state: " + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) { + state = adapter.getState(); + if (state == BluetoothAdapter.STATE_OFF) { + assertFalse(adapter.isEnabled()); + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + return; + } + } else { + assertFalse(adapter.isEnabled()); + assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail("disable() timeout: " + + "state=" + state + " (expected " + BluetoothAdapter.STATE_OFF + ") " + + "flags=" + firedFlags + " (expected " + mask + ")"); + } + + private void enable(BluetoothAdapter adapter) { + int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG; + mReceiver.resetFiredFlags(); + + int state = adapter.getState(); + switch (state) { + case BluetoothAdapter.STATE_ON: + assertTrue(adapter.isEnabled()); + return; + case BluetoothAdapter.STATE_OFF: + case BluetoothAdapter.STATE_TURNING_OFF: + assertFalse(adapter.isEnabled()); + assertTrue(adapter.enable()); + break; + case BluetoothAdapter.STATE_TURNING_ON: + assertFalse(adapter.isEnabled()); + mask = 0; // Don't check for received intents since we might have missed them. + break; + default: + fail("enable() invalid state: state=" + state); + } + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) { + state = adapter.getState(); + if (state == BluetoothAdapter.STATE_ON) { + assertTrue(adapter.isEnabled()); + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + return; + } + } else { + assertFalse(adapter.isEnabled()); + assertEquals(BluetoothAdapter.STATE_TURNING_ON, state); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail("enable() timeout: " + + "state=" + state + " (expected " + BluetoothAdapter.STATE_OFF + ") " + + "flags=" + firedFlags + " (expected " + mask + ")"); + } + + private void discoverable(BluetoothAdapter adapter) { + int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("discoverable() bluetooth not enabled"); + } + + int scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + return; + } + + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE); + assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) { + scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + return; + } + } else { + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail("discoverable() timeout: " + + "scanMode=" + scanMode + " (expected " + + BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE + ") " + + "flags=" + firedFlags + " (expected " + mask + ")"); + } + + private void undiscoverable(BluetoothAdapter adapter) { + int mask = SCAN_MODE_CONNECTABLE_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("undiscoverable(): bluetooth not enabled"); + } + + int scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + return; + } + + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE)); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) { + scanMode = adapter.getScanMode(); + if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) { + if ((mReceiver.getFiredFlags() & mask) == mask) { + mReceiver.resetFiredFlags(); + return; + } + } else { + assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail("undiscoverable() timeout: " + + "scanMode=" + scanMode + " (expected " + + BluetoothAdapter.SCAN_MODE_CONNECTABLE + ") " + + "flags=" + firedFlags + " (expected " + mask + ")"); + } + + private void startScan(BluetoothAdapter adapter) { + int mask = DISCOVERY_STARTED_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("startScan(): bluetooth not enabled"); + } + + if (adapter.isDiscovering()) { + return; + } + + assertTrue(adapter.startDiscovery()); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) { + if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) { + mReceiver.resetFiredFlags(); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail("startScan() timeout: " + + "isDiscovering=" + adapter.isDiscovering() + " " + + "flags=" + firedFlags + " (expected " + mask + ")"); + } + + private void stopScan(BluetoothAdapter adapter) { + int mask = DISCOVERY_FINISHED_FLAG; + mReceiver.resetFiredFlags(); + + if (!adapter.isEnabled()) { + fail("stopScan(): bluetooth not enabled"); + } + + if (!adapter.isDiscovering()) { + return; + } + + // TODO: put assertTrue() around cancelDiscovery() once it starts + // returning true. + adapter.cancelDiscovery(); + + long s = System.currentTimeMillis(); + while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) { + if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) { + mReceiver.resetFiredFlags(); + return; + } + sleep(POLL_TIME); + } + + int firedFlags = mReceiver.getFiredFlags(); + mReceiver.resetFiredFlags(); + fail("stopScan() timeout: " + + "isDiscovering=" + adapter.isDiscovering() + " " + + "flags=" + firedFlags + " (expected " + mask + ")"); + } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + } + } +} |