diff options
Diffstat (limited to 'core/java/android')
26 files changed, 668 insertions, 231 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 41e3fdf..034e3c7 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1638,6 +1638,12 @@ public class Activity extends ContextThemeWrapper * or later, consider instead using {@link LoaderManager} instead, available * via {@link #getLoaderManager()}.</em> * + * <p><strong>Warning:</strong> Do not call {@link Cursor#close()} on a cursor obtained using + * this method, because the activity will do that for you at the appropriate time. However, if + * you call {@link #stopManagingCursor} on a cursor from a managed query, the system <em>will + * not</em> automatically close the cursor and, in that case, you must call + * {@link Cursor#close()}.</p> + * * @param uri The URI of the content provider to query. * @param projection List of columns to return. * @param selection SQL WHERE clause. @@ -1672,6 +1678,12 @@ public class Activity extends ContextThemeWrapper * or later, consider instead using {@link LoaderManager} instead, available * via {@link #getLoaderManager()}.</em> * + * <p><strong>Warning:</strong> Do not call {@link Cursor#close()} on a cursor obtained using + * this method, because the activity will do that for you at the appropriate time. However, if + * you call {@link #stopManagingCursor} on a cursor from a managed query, the system <em>will + * not</em> automatically close the cursor and, in that case, you must call + * {@link Cursor#close()}.</p> + * * @param uri The URI of the content provider to query. * @param projection List of columns to return. * @param selection SQL WHERE clause. @@ -1707,6 +1719,12 @@ public class Activity extends ContextThemeWrapper * or later, consider instead using {@link LoaderManager} instead, available * via {@link #getLoaderManager()}.</em> * + * <p><strong>Warning:</strong> Do not call {@link Cursor#close()} on cursor obtained from + * {@link #managedQuery}, because the activity will do that for you at the appropriate time. + * However, if you call {@link #stopManagingCursor} on a cursor from a managed query, the system + * <em>will not</em> automatically close the cursor and, in that case, you must call + * {@link Cursor#close()}.</p> + * * @param c The Cursor to be managed. * * @see #managedQuery(android.net.Uri , String[], String, String[], String) @@ -1728,6 +1746,10 @@ public class Activity extends ContextThemeWrapper * {@link #startManagingCursor}, stop the activity's management of that * cursor. * + * <p><strong>Warning:</strong> After calling this method on a cursor from a managed query, + * the system <em>will not</em> automatically close the cursor and you must call + * {@link Cursor#close()}.</p> + * * @param c The Cursor that was being managed. * * @see #startManagingCursor diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 102fac1..4fe9cef 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1254,6 +1254,12 @@ public class ActivityManager { */ public ComponentName importanceReasonComponent; + /** + * When {@link importanceReasonPid} is non-0, this is the importance + * of the other pid. @hide + */ + public int importanceReasonImportance; + public RunningAppProcessInfo() { importance = IMPORTANCE_FOREGROUND; importanceReasonCode = REASON_UNKNOWN; @@ -1280,6 +1286,7 @@ public class ActivityManager { dest.writeInt(importanceReasonCode); dest.writeInt(importanceReasonPid); ComponentName.writeToParcel(importanceReasonComponent, dest); + dest.writeInt(importanceReasonImportance); } public void readFromParcel(Parcel source) { @@ -1293,6 +1300,7 @@ public class ActivityManager { importanceReasonCode = source.readInt(); importanceReasonPid = source.readInt(); importanceReasonComponent = ComponentName.readFromParcel(source); + importanceReasonImportance = source.readInt(); } public static final Creator<RunningAppProcessInfo> CREATOR = diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java index 0a01dcf..c165d92 100644 --- a/core/java/android/bluetooth/BluetoothHealth.java +++ b/core/java/android/bluetooth/BluetoothHealth.java @@ -32,10 +32,25 @@ import java.util.List; * <p>BluetoothHealth is a proxy object for controlling the Bluetooth * Service via IPC. * - * <p> Use {@link BluetoothAdapter#getProfileProxy} to get - * the BluetoothHealth proxy object. Use - * {@link BluetoothAdapter#closeProfileProxy} to close the service connection. - * @hide + * <p> How to connect to a health device which is acting in the source role. + * <li> Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothHealth proxy object. </li> + * <li> Create an {@link BluetoothHealth} callback and call + * {@link #registerSinkAppConfiguration} to register an application + * configuration </li> + * <li> Pair with the remote device. This currently needs to be done manually + * from Bluetooth Settings </li> + * <li> Connect to a health device using {@link #connectChannelToSource}. Some + * devices will connect the channel automatically. The {@link BluetoothHealth} + * callback will inform the application of channel state change. </li> + * <li> Use the file descriptor provided with a connected channel to read and + * write data to the health channel. </li> + * <li> The received data needs to be interpreted using a health manager which + * implements the IEEE 11073-xxxxx specifications. + * <li> When done, close the health channel by calling {@link #disconnectChannel} + * and unregister the application configuration calling + * {@link #unregisterAppConfiguration} + * */ public final class BluetoothHealth implements BluetoothProfile { private static final String TAG = "BluetoothHealth"; @@ -137,7 +152,6 @@ public final class BluetoothHealth implements BluetoothProfile { * * @param config The health app configuration * @return Success or failure. - * @hide */ public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { boolean result = false; @@ -222,16 +236,15 @@ public final class BluetoothHealth implements BluetoothProfile { * @param device The remote Bluetooth device. * @param config The application configuration which has been registered using * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } - * @param fd The file descriptor that was associated with the channel. + * @param channelId The channel id associated with the channel * @return If true, the callback associated with the application config will be called. - * @hide */ public boolean disconnectChannel(BluetoothDevice device, - BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { + BluetoothHealthAppConfiguration config, int channelId) { if (mService != null && isEnabled() && isValidDevice(device) && config != null) { try { - return mService.disconnectChannel(device, config, fd); + return mService.disconnectChannel(device, config, channelId); } catch (RemoteException e) { Log.e(TAG, e.toString()); } @@ -248,11 +261,13 @@ public final class BluetoothHealth implements BluetoothProfile { * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * + * <p> Its the responsibility of the caller to close the ParcelFileDescriptor + * when done. + * * @param device The remote Bluetooth health device * @param config The application configuration * @return null on failure, ParcelFileDescriptor on success. */ - public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { if (mService != null && isEnabled() && isValidDevice(device) && @@ -300,7 +315,7 @@ public final class BluetoothHealth implements BluetoothProfile { } /** - * Get connected devices for this specific profile. + * Get connected devices for the health profile. * * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} * @@ -368,14 +383,15 @@ public final class BluetoothHealth implements BluetoothProfile { @Override public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, int status) { - mCallback.onHealthAppConfigurationStatusChange(config, status); + mCallback.onHealthAppConfigurationStatusChange(config, status); } @Override public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd) { - mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd); + ParcelFileDescriptor fd, int channelId) { + mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd, + channelId); } } @@ -389,13 +405,13 @@ public final class BluetoothHealth implements BluetoothProfile { public static final int STATE_CHANNEL_DISCONNECTING = 3; /** Health App Configuration registration success */ - public static final int APPLICATION_REGISTRATION_SUCCESS = 0; + public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; /** Health App Configuration registration failure */ - public static final int APPLICATION_REGISTRATION_FAILURE = 1; + public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; /** Health App Configuration un-registration success */ - public static final int APPLICATION_UNREGISTRATION_SUCCESS = 2; + public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; /** Health App Configuration un-registration failure */ - public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; + public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; private ServiceListener mServiceListener; private IBluetooth mService; diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java index 7020249..15a9101 100644 --- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java +++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java @@ -26,7 +26,6 @@ import android.os.Parcelable; * that the Bluetooth Health third party application will register to communicate with the * remote Bluetooth health device. * - * @hide */ public final class BluetoothHealthAppConfiguration implements Parcelable { private final String mName; @@ -39,6 +38,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * * @param name Friendly name associated with the application configuration * @param dataType Data Type of the remote Bluetooth Health device + * @hide */ BluetoothHealthAppConfiguration(String name, int dataType) { mName = name; @@ -54,6 +54,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * @param dataType Data Type of the remote Bluetooth Health device * @param role {@link BluetoothHealth#SOURCE_ROLE} or * {@link BluetoothHealth#SINK_ROLE} + * @hide */ BluetoothHealthAppConfiguration(String name, int dataType, int role, int channelType) { @@ -93,7 +94,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { mChannelType + "]"; } - @Override public int describeContents() { return 0; } @@ -132,6 +132,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or * {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or * {@link BluetoothHealth#CHANNEL_TYPE_ANY}. + * @hide */ public int getChannelType() { return mChannelType; @@ -155,13 +156,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable { } }; - @Override public void writeToParcel(Parcel out, int flags) { out.writeString(mName); out.writeInt(mDataType); out.writeInt(mRole); out.writeInt(mChannelType); } - - } diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java index 0d11bb5..baf2ade 100644 --- a/core/java/android/bluetooth/BluetoothHealthCallback.java +++ b/core/java/android/bluetooth/BluetoothHealthCallback.java @@ -21,22 +21,48 @@ import android.os.ParcelFileDescriptor; import android.util.Log; /** - * This class is used for all the {@link BluetoothHealth} callbacks. - * @hide + * This abstract class is used to implement {@link BluetoothHealth} callbacks. */ public abstract class BluetoothHealthCallback { - private static final String TAG = "BluetoothHealthCallback"; + /** + * Callback to inform change in registration state of the health + * application. + * <p> This callback is called on the binder thread (not on the UI thread) + * + * @param config Bluetooth Health app configuration + * @param status Success or failure of the registration or unregistration + * calls. Can be one of + * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or + * {@link BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or + * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or + * {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE} + */ public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, - int status) { - Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status); + int status) { + Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); } + /** + * Callback to inform change in channel state. + * <p> Its the responsibility of the implementor of this callback to close the + * parcel file descriptor when done. This callback is called on the Binder + * thread (not the UI thread) + * + * @param config The Health app configutation + * @param device The Bluetooth Device + * @param prevState The previous state of the channel + * @param newState The new state of the channel. + * @param fd The Parcel File Descriptor when the channel state is connected. + * @param channelId The id associated with the channel. This id will be used + * in future calls like when disconnecting the channel. + */ public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int newState, - ParcelFileDescriptor fd) { - Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device + - "PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd); + BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd, + int channelId) { + Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device + + "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd + + "ChannelId:" + channelId); } } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index d4e7f7d..fefeb93 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -116,7 +116,7 @@ interface IBluetooth boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int channelType); - boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, in ParcelFileDescriptor fd); + boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id); ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); List<BluetoothDevice> getConnectedHealthDevices(); List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(in int[] states); diff --git a/core/java/android/bluetooth/IBluetoothHealthCallback.aidl b/core/java/android/bluetooth/IBluetoothHealthCallback.aidl index 9fe5335..0ace9fe 100644 --- a/core/java/android/bluetooth/IBluetoothHealthCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothHealthCallback.aidl @@ -27,5 +27,6 @@ interface IBluetoothHealthCallback { void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, - in BluetoothDevice device, int prevState, int newState, in ParcelFileDescriptor fd); + in BluetoothDevice device, int prevState, int newState, in + ParcelFileDescriptor fd, int id); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 46712a9..48f94d0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -239,6 +239,9 @@ public abstract class Context { * methods of activities and other components are called. Note that you * <em>must</em> be sure to use {@link #unregisterComponentCallbacks} when * appropriate in the future; this will not be removed for you. + * + * @param callback The interface to call. This can be either a + * {@link ComponentCallbacks} or {@link ComponentCallbacks2} interface. */ public void registerComponentCallbacks(ComponentCallbacks callback) { getApplicationContext().registerComponentCallbacks(callback); diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index ef6e131..d4ed4b9 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -192,10 +192,10 @@ public class SyncManager implements OnAccountsUpdateListener { private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours /** - * The amount of time to wait after attempting a bind before canceling a sync and disabling - * the sync adapter + * The amount of time (in milliseconds) to wait after attempting a bind + * before canceling a sync and disabling the sync adapter */ - public static final long BIND_TIMEOUT_MS = 30 * 1000; + public static final long BIND_TIMEOUT_MS = 5 * 60 * 1000; public void onAccountsUpdated(Account[] accounts) { // remember if this was the first time this was called after an update diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 44e7e52..4fc63ed 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -19,6 +19,7 @@ package android.inputmethodservice; import android.content.Context; import android.util.AttributeSet; import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.InputMethodManager; import android.widget.EditText; /*** @@ -142,4 +143,17 @@ public class ExtractEditText extends EditText { @Override public boolean hasFocus() { return this.isEnabled(); } + + /** + * @hide + */ + @Override protected void viewClicked(InputMethodManager imm) { + // As an instance of this class is supposed to be owned by IMS, + // and it has a reference to the IMS (the current IME), + // we just need to call back its onViewClicked() here. + // It should be good to avoid unnecessary IPCs by doing this as well. + if (mIME != null) { + mIME.onViewClicked(false); + } + } } diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 88abf1a..65c1bd5 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -195,6 +195,8 @@ public class MobileDataStateTracker implements NetworkStateTracker { intent.getStringExtra(Phone.STATE_KEY)); String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); + mNetworkInfo.setRoaming(intent.getBooleanExtra(Phone.DATA_NETWORK_ROAMING_KEY, + false)); mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY, false)); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 3918cfd..f3be39c 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -102,6 +102,21 @@ public class NetworkStats implements Parcelable { this.txPackets = txPackets; this.operations = operations; } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("iface=").append(iface); + builder.append(" uid=").append(uid); + builder.append(" set=").append(setToString(set)); + builder.append(" tag=").append(tagToString(tag)); + builder.append(" rxBytes=").append(rxBytes); + builder.append(" rxPackets=").append(rxPackets); + builder.append(" txBytes=").append(txBytes); + builder.append(" txPackets=").append(txPackets); + builder.append(" operations=").append(operations); + return builder.toString(); + } } public NetworkStats(long elapsedRealtime, int initialSize) { diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 1f2b342..ca1d0d9 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -34,6 +34,7 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.graphics.Rect; import android.net.Uri; +import android.os.Bundle; import android.os.RemoteException; import android.text.TextUtils; import android.util.DisplayMetrics; @@ -44,6 +45,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * <p> @@ -167,6 +171,22 @@ public final class ContactsContract { public static final String STREQUENT_PHONE_ONLY = "strequent_phone_only"; /** + * A key to a boolean in the "extras" bundle of the cursor. + * The boolean indicates that the provider did not create a snippet and that the client asking + * for the snippet should do it (true means the snippeting was deferred to the client). + * + * @hide + */ + public static final String DEFERRED_SNIPPETING = "deferred_snippeting"; + + /** + * Key to retrieve the original query on the client side. + * + * @hide + */ + public static final String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query"; + + /** * @hide */ public static final class Preferences { @@ -4357,6 +4377,12 @@ public final class ContactsContract { Uri.withAppendedPath(AUTHORITY_URI, "raw_contact_entities"); /** + * The content:// style URI for this table, specific to the user's profile. + */ + public static final Uri PROFILE_CONTENT_URI = + Uri.withAppendedPath(Profile.CONTENT_URI, "raw_contact_entities"); + + /** * The MIME type of {@link #CONTENT_URI} providing a directory of raw contact entities. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact_entity"; @@ -4857,6 +4883,19 @@ public final class ContactsContract { * @hide */ public static final String SNIPPET_ARGS_PARAM_KEY = "snippet_args"; + + /** + * A key to ask the provider to defer the snippeting to the client if possible. + * Value of 1 implies true, 0 implies false when 0 is the default. + * When a cursor is returned to the client, it should check for an extra with the name + * {@link ContactsContract#DEFERRED_SNIPPETING} in the cursor. If it exists, the client + * should do its own snippeting using {@link ContactsContract#snippetize}. If + * it doesn't exist, the snippet column in the cursor should already contain a snippetized + * string. + * + * @hide + */ + public static final String DEFERRED_SNIPPETING_KEY = "deferred_snippeting"; } /** @@ -7053,6 +7092,18 @@ public final class ContactsContract { public static final String ACCOUNT_TYPE = "account_type"; /** + * The data set within the account that this row belongs to. This allows + * multiple sync adapters for the same account type to distinguish between + * each others' data. + * + * This is empty by default, and is completely optional. It only needs to + * be populated if multiple sync adapters are entering distinct data for + * the same account type and account name. + * <P>Type: TEXT</P> + */ + public static final String DATA_SET = "data_set"; + + /** * Depending on the mode defined by the sync-adapter, this flag controls * the top-level sync behavior for this data source. * <p> @@ -8054,4 +8105,138 @@ public final class ContactsContract { public static final String DATA_SET = "com.android.contacts.extra.DATA_SET"; } } + + /** + * Creates a snippet out of the given content that matches the given query. + * @param content - The content to use to compute the snippet. + * @param displayName - Display name for the contact - if this already contains the search + * content, no snippet should be shown. + * @param query - String to search for in the content. + * @param snippetStartMatch - Marks the start of the matching string in the snippet. + * @param snippetEndMatch - Marks the end of the matching string in the snippet. + * @param snippetEllipsis - Ellipsis string appended to the end of the snippet (if too long). + * @param snippetMaxTokens - Maximum number of words from the snippet that will be displayed. + * @return The computed snippet, or null if the snippet could not be computed or should not be + * shown. + * + * @hide + */ + public static String snippetize(String content, String displayName, String query, + char snippetStartMatch, char snippetEndMatch, String snippetEllipsis, + int snippetMaxTokens) { + + String lowerQuery = query != null ? query.toLowerCase() : null; + if (TextUtils.isEmpty(content) || TextUtils.isEmpty(query) || + TextUtils.isEmpty(displayName) || !content.toLowerCase().contains(lowerQuery)) { + return null; + } + + // If the display name already contains the query term, return empty - snippets should + // not be needed in that case. + String lowerDisplayName = displayName != null ? displayName.toLowerCase() : ""; + List<String> nameTokens = new ArrayList<String>(); + List<Integer> nameTokenOffsets = new ArrayList<Integer>(); + split(lowerDisplayName.trim(), nameTokens, nameTokenOffsets); + for (String nameToken : nameTokens) { + if (nameToken.startsWith(lowerQuery)) { + return null; + } + } + + String[] contentLines = content.split("\n"); + + // Locate the lines of the content that contain the query term. + for (String contentLine : contentLines) { + if (contentLine.toLowerCase().contains(lowerQuery)) { + + // Line contains the query string - now search for it at the start of tokens. + List<String> lineTokens = new ArrayList<String>(); + List<Integer> tokenOffsets = new ArrayList<Integer>(); + split(contentLine.trim(), lineTokens, tokenOffsets); + + // As we find matches against the query, we'll populate this list with the marked + // (or unchanged) tokens. + List<String> markedTokens = new ArrayList<String>(); + + int firstToken = -1; + int lastToken = -1; + for (int i = 0; i < lineTokens.size(); i++) { + String token = lineTokens.get(i); + String lowerToken = token.toLowerCase(); + if (lowerToken.startsWith(lowerQuery)) { + + // Query term matched; surround the token with match markers. + markedTokens.add(snippetStartMatch + token + snippetEndMatch); + + // If this is the first token found with a match, mark the token + // positions to use for assembling the snippet. + if (firstToken == -1) { + firstToken = + Math.max(0, i - (int) Math.floor( + Math.abs(snippetMaxTokens) + / 2.0)); + lastToken = + Math.min(lineTokens.size(), firstToken + + Math.abs(snippetMaxTokens)); + } + } else { + markedTokens.add(token); + } + } + + // Assemble the snippet by piecing the tokens back together. + if (firstToken > -1) { + StringBuilder sb = new StringBuilder(); + if (firstToken > 0) { + sb.append(snippetEllipsis); + } + for (int i = firstToken; i < lastToken; i++) { + String markedToken = markedTokens.get(i); + String originalToken = lineTokens.get(i); + sb.append(markedToken); + if (i < lastToken - 1) { + // Add the characters that appeared between this token and the next. + sb.append(contentLine.substring( + tokenOffsets.get(i) + originalToken.length(), + tokenOffsets.get(i + 1))); + } + } + if (lastToken < lineTokens.size()) { + sb.append(snippetEllipsis); + } + return sb.toString(); + } + } + } + return null; + } + + /** + * Pattern for splitting a line into tokens. This matches e-mail addresses as a single token, + * otherwise splitting on any group of non-alphanumeric characters. + * + * @hide + */ + private static Pattern SPLIT_PATTERN = + Pattern.compile("([\\w-\\.]+)@((?:[\\w]+\\.)+)([a-zA-Z]{2,4})|[\\w]+"); + + /** + * Helper method for splitting a string into tokens. The lists passed in are populated with the + * tokens and offsets into the content of each token. The tokenization function parses e-mail + * addresses as a single token; otherwise it splits on any non-alphanumeric character. + * @param content Content to split. + * @param tokens List of token strings to populate. + * @param offsets List of offsets into the content for each token returned. + * + * @hide + */ + private static void split(String content, List<String> tokens, List<Integer> offsets) { + Matcher matcher = SPLIT_PATTERN.matcher(content); + while (matcher.find()) { + tokens.add(matcher.group()); + offsets.add(matcher.start()); + } + } + + } diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java index 69fbca3..ac46ee2 100644 --- a/core/java/android/server/BluetoothAdapterStateMachine.java +++ b/core/java/android/server/BluetoothAdapterStateMachine.java @@ -109,6 +109,8 @@ final class BluetoothAdapterStateMachine extends StateMachine { private static final int DEVICES_DISCONNECT_TIMEOUT = 104; // Prepare Bluetooth timeout happens private static final int PREPARE_BLUETOOTH_TIMEOUT = 105; + // Bluetooth Powerdown timeout happens + private static final int POWER_DOWN_TIMEOUT = 106; private Context mContext; private BluetoothService mBluetoothService; @@ -129,6 +131,8 @@ final class BluetoothAdapterStateMachine extends StateMachine { private static final int PREPARE_BLUETOOTH_TIMEOUT_TIME = 10000; + private static final int POWER_DOWN_TIMEOUT_TIME = 5000; + BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService, BluetoothAdapter bluetoothAdapter) { super(TAG); @@ -386,6 +390,11 @@ final class BluetoothAdapterStateMachine extends StateMachine { break; case USER_TURN_OFF: // ignore break; + case POWER_STATE_CHANGED: + if ((Boolean) message.obj) { + recoverStateMachine(TURN_HOT, null); + } + break; default: return NOT_HANDLED; } @@ -420,14 +429,27 @@ final class BluetoothAdapterStateMachine extends StateMachine { } break; case POWER_STATE_CHANGED: + removeMessages(POWER_DOWN_TIMEOUT); if (!((Boolean) message.obj)) { - transitionTo(mHotOff); - finishSwitchingOff(); + if (mPublicState == BluetoothAdapter.STATE_TURNING_OFF) { + transitionTo(mHotOff); + finishSwitchingOff(); + } + } else { + if (mPublicState != BluetoothAdapter.STATE_TURNING_ON) { + if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { + recoverStateMachine(TURN_HOT, null); + } else { + recoverStateMachine(TURN_COLD, null); + } + } } break; case ALL_DEVICES_DISCONNECTED: removeMessages(DEVICES_DISCONNECT_TIMEOUT); mBluetoothService.switchConnectable(false); + sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); break; case DEVICES_DISCONNECT_TIMEOUT: sendMessage(ALL_DEVICES_DISCONNECTED); @@ -439,6 +461,17 @@ final class BluetoothAdapterStateMachine extends StateMachine { deferMessage(obtainMessage(TURN_HOT)); } break; + case POWER_DOWN_TIMEOUT: + transitionTo(mHotOff); + finishSwitchingOff(); + // reset the hardware for error recovery + Log.e(TAG, "Devices failed to power down, reseting..."); + deferMessage(obtainMessage(TURN_COLD)); + if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { + deferMessage(obtainMessage(TURN_HOT)); + } + break; case USER_TURN_ON: case AIRPLANE_MODE_OFF: case AIRPLANE_MODE_ON: @@ -501,6 +534,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { DEVICES_DISCONNECT_TIMEOUT_TIME); } else { mBluetoothService.switchConnectable(false); + sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); } // we turn all the way to PowerOff with AIRPLANE_MODE_ON @@ -520,6 +554,12 @@ final class BluetoothAdapterStateMachine extends StateMachine { case PER_PROCESS_TURN_OFF: perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); break; + case POWER_STATE_CHANGED: + if ((Boolean) message.obj) { + // reset the state machine and send it TURN_ON_CONTINUE message + recoverStateMachine(USER_TURN_ON, false); + } + break; default: return NOT_HANDLED; } @@ -540,7 +580,7 @@ final class BluetoothAdapterStateMachine extends StateMachine { if (what == PER_PROCESS_TURN_ON) { isTurningOn = true; - } else if (what == PER_PROCESS_TURN_OFF) { + } else if (what == USER_TURN_OFF) { isTurningOn = false; } else { Log.e(TAG, "enter PerProcessState: wrong msg: " + what); @@ -568,12 +608,31 @@ final class BluetoothAdapterStateMachine extends StateMachine { } break; case POWER_STATE_CHANGED: + removeMessages(POWER_DOWN_TIMEOUT); if (!((Boolean) message.obj)) { transitionTo(mHotOff); if (!mContext.getResources().getBoolean (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { deferMessage(obtainMessage(TURN_COLD)); } + } else { + if (!isTurningOn) { + recoverStateMachine(TURN_COLD, null); + for (IBluetoothStateChangeCallback c: + mBluetoothService.getApplicationStateChangeCallbacks()) { + perProcessCallback(false, c); + deferMessage(obtainMessage(PER_PROCESS_TURN_ON, c)); + } + } + } + break; + case POWER_DOWN_TIMEOUT: + transitionTo(mHotOff); + Log.e(TAG, "Power-down timed out, resetting..."); + deferMessage(obtainMessage(TURN_COLD)); + if (mContext.getResources().getBoolean + (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) { + deferMessage(obtainMessage(TURN_HOT)); } break; case USER_TURN_ON: @@ -616,10 +675,12 @@ final class BluetoothAdapterStateMachine extends StateMachine { perProcessCallback(false, (IBluetoothStateChangeCallback)message.obj); if (mBluetoothService.isApplicationStateChangeTrackerEmpty()) { mBluetoothService.switchConnectable(false); + sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); } break; case AIRPLANE_MODE_ON: mBluetoothService.switchConnectable(false); + sendMessageDelayed(POWER_DOWN_TIMEOUT, POWER_DOWN_TIMEOUT_TIME); allProcessesCallback(false); // we turn all the way to PowerOff with AIRPLANE_MODE_ON deferMessage(obtainMessage(AIRPLANE_MODE_ON)); @@ -699,6 +760,17 @@ final class BluetoothAdapterStateMachine extends StateMachine { mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); } + /** + * bluetoothd has crashed and recovered, the adapter state machine has to + * reset itself and try to return to previous state + */ + private void recoverStateMachine(int what, Object obj) { + Log.e(TAG, "Get unexpected power on event, reset with: " + what); + transitionTo(mHotOff); + deferMessage(obtainMessage(TURN_COLD)); + deferMessage(obtainMessage(what, obj)); + } + private void dump(PrintWriter pw) { IState currentState = getCurrentState(); if (currentState == mPowerOff) { diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 6eff796..e4f2d32 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -59,9 +59,8 @@ class BluetoothEventLoop { // from remote device when Android is in Suspend state. private PowerManager.WakeLock mWakeLock; - private static final int EVENT_RESTART_BLUETOOTH = 1; - private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 2; - private static final int EVENT_AGENT_CANCEL = 3; + private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 1; + private static final int EVENT_AGENT_CANCEL = 2; private static final int CREATE_DEVICE_ALREADY_EXISTS = 1; private static final int CREATE_DEVICE_SUCCESS = 0; @@ -75,9 +74,6 @@ class BluetoothEventLoop { public void handleMessage(Message msg) { String address = null; switch (msg.what) { - case EVENT_RESTART_BLUETOOTH: - mBluetoothService.restart(); - break; case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT: address = (String)msg.obj; if (address != null) { @@ -375,9 +371,6 @@ class BluetoothEventLoop { } else if (name.equals("Powered")) { mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED, propValues[1].equals("true") ? new Boolean(true) : new Boolean(false)); - // bluetoothd has restarted, re-read all our properties. - // Note: bluez only sends this property change when it restarts. - onRestartRequired(); } else if (name.equals("DiscoverableTimeout")) { adapterProperties.setProperty(name, propValues[1]); } @@ -1033,14 +1026,6 @@ class BluetoothEventLoop { mBluetoothService.onHealthDeviceChannelChanged(devicePath, channelPath, exists); } - private void onRestartRequired() { - if (mBluetoothService.isEnabled()) { - Log.e(TAG, "*** A serious error occurred (did bluetoothd crash?) - " + - "restarting Bluetooth ***"); - mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH); - } - } - private static void log(String msg) { Log.d(TAG, msg); } diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java index 51c995e..a6ada2b 100644 --- a/core/java/android/server/BluetoothHealthProfileHandler.java +++ b/core/java/android/server/BluetoothHealthProfileHandler.java @@ -29,6 +29,8 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import java.io.FileDescriptor; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -47,7 +49,6 @@ final class BluetoothHealthProfileHandler { private static final boolean DBG = true; private static BluetoothHealthProfileHandler sInstance; - private Context mContext; private BluetoothService mBluetoothService; private ArrayList<HealthChannel> mHealthChannels; private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs; @@ -76,6 +77,17 @@ final class BluetoothHealthProfileHandler { mConfig = config; mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mChannelPath == null ? 0 : mChannelPath.hashCode()); + result = 31 * result + mDevice.hashCode(); + result = 31 * result + mConfig.hashCode(); + result = 31 * result + mState; + result = 31 * result + mChannelType; + return result; + } } private final Handler mHandler = new Handler() { @@ -98,28 +110,38 @@ final class BluetoothHealthProfileHandler { } if (path == null) { - mCallbacks.remove(registerApp); callHealthApplicationStatusCallback(registerApp, - BluetoothHealth.APPLICATION_REGISTRATION_FAILURE); + BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE); + mCallbacks.remove(registerApp); } else { mHealthAppConfigs.put(registerApp, path); callHealthApplicationStatusCallback(registerApp, - BluetoothHealth.APPLICATION_REGISTRATION_SUCCESS); + BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS); } break; case MESSAGE_UNREGISTER_APPLICATION: BluetoothHealthAppConfiguration unregisterApp = (BluetoothHealthAppConfiguration) msg.obj; + + // Disconnect all the channels + for (HealthChannel chan : mHealthChannels) { + if (chan.mConfig.equals(unregisterApp) && + chan.mState != BluetoothHealth.STATE_CHANNEL_DISCONNECTED) { + disconnectChannel(chan.mDevice, unregisterApp, chan.hashCode()); + } + } + boolean result = mBluetoothService.unregisterHealthApplicationNative( mHealthAppConfigs.get(unregisterApp)); if (result) { - mCallbacks.remove(unregisterApp); callHealthApplicationStatusCallback(unregisterApp, - BluetoothHealth.APPLICATION_UNREGISTRATION_SUCCESS); + BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS); + mCallbacks.remove(unregisterApp); + mHealthAppConfigs.remove(unregisterApp); } else { callHealthApplicationStatusCallback(unregisterApp, - BluetoothHealth.APPLICATION_UNREGISTRATION_FAILURE); + BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE); } break; case MESSAGE_CONNECT_CHANNEL: @@ -133,7 +155,8 @@ final class BluetoothHealthProfileHandler { channelType)) { int prevState = chan.mState; int state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; - callHealthChannelCallback(chan.mConfig, chan.mDevice, prevState, state, null); + callHealthChannelCallback(chan.mConfig, chan.mDevice, prevState, state, null, + chan.hashCode()); mHealthChannels.remove(chan); } } @@ -141,7 +164,6 @@ final class BluetoothHealthProfileHandler { }; private BluetoothHealthProfileHandler(Context context, BluetoothService service) { - mContext = context; mBluetoothService = service; mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>(); mHealthChannels = new ArrayList<HealthChannel>(); @@ -205,7 +227,7 @@ final class BluetoothHealthProfileHandler { int prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; int state = BluetoothHealth.STATE_CHANNEL_CONNECTING; - callHealthChannelCallback(config, device, prevState, state, null); + callHealthChannelCallback(config, device, prevState, state, null, chan.hashCode()); Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_CHANNEL); msg.obj = chan; @@ -235,37 +257,44 @@ final class BluetoothHealthProfileHandler { } boolean disconnectChannel(BluetoothDevice device, - BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { - HealthChannel chan = findChannelByFd(device, config, fd); - if (chan == null) return false; + BluetoothHealthAppConfiguration config, int id) { + HealthChannel chan = findChannelById(device, config, id); + if (chan == null) { + return false; + } String deviceObjectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); - if (mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) { - int prevState = chan.mState; - chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING; + + mBluetoothService.releaseChannelFdNative(chan.mChannelPath); + + int prevState = chan.mState; + chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING; + callHealthChannelCallback(config, device, prevState, chan.mState, + null, chan.hashCode()); + + if (!mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) { + prevState = chan.mState; + chan.mState = BluetoothHealth.STATE_CHANNEL_CONNECTED; callHealthChannelCallback(config, device, prevState, chan.mState, - chan.mChannelFd); - return true; - } else { + chan.mChannelFd, chan.hashCode()); return false; + } else { + return true; } } - private HealthChannel findChannelByFd(BluetoothDevice device, - BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { + private HealthChannel findChannelById(BluetoothDevice device, + BluetoothHealthAppConfiguration config, int id) { for (HealthChannel chan : mHealthChannels) { - if (chan.mChannelFd.equals(fd) && chan.mDevice.equals(device) && - chan.mConfig.equals(config)) return chan; + if (chan.hashCode() == id) return chan; } return null; } - private HealthChannel findChannelByPath(BluetoothDevice device, - BluetoothHealthAppConfiguration config, String path) { + private HealthChannel findChannelByPath(BluetoothDevice device, String path) { for (HealthChannel chan : mHealthChannels) { - if (chan.mChannelPath.equals(path) && chan.mDevice.equals(device) && - chan.mConfig.equals(config)) return chan; + if (path.equals(chan.mChannelPath) && device.equals(chan.mDevice)) return chan; } return null; } @@ -296,7 +325,15 @@ final class BluetoothHealthProfileHandler { ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, BluetoothHealthAppConfiguration config) { HealthChannel chan = getMainChannel(device, config); - if (chan != null) return chan.mChannelFd; + if (chan != null) { + ParcelFileDescriptor pfd = null; + try { + pfd = chan.mChannelFd.dup(); + return pfd; + } catch (IOException e) { + return null; + } + } String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); @@ -308,14 +345,18 @@ final class BluetoothHealthProfileHandler { // We had no record of the main channel but querying Bluez we got a // main channel. We might not have received the PropertyChanged yet for // the main channel creation so update our data structure here. - chan = findChannelByPath(device, config, mainChannelPath); + chan = findChannelByPath(device, mainChannelPath); if (chan == null) { errorLog("Main Channel present but we don't have any account of it:" + device +":" + config); return null; } chan.mMainChannel = true; - return chan.mChannelFd; + try { + return chan.mChannelFd.dup(); + } catch (IOException e) { + return null; + } } /*package*/ void onHealthDevicePropertyChanged(String devicePath, @@ -334,7 +375,7 @@ final class BluetoothHealthProfileHandler { BluetoothHealthAppConfiguration config = findHealthApplication(device, channelPath); if (config != null) { - HealthChannel chan = findChannelByPath(device, config, channelPath); + HealthChannel chan = findChannelByPath(device, channelPath); if (chan == null) { errorLog("Health Channel is not present:" + channelPath); } else { @@ -346,21 +387,22 @@ final class BluetoothHealthProfileHandler { private BluetoothHealthAppConfiguration findHealthApplication( BluetoothDevice device, String channelPath) { BluetoothHealthAppConfiguration config = null; - String configPath = mBluetoothService.getChannelApplicationNative(channelPath); + HealthChannel chan = findChannelByPath(device, channelPath); - if (configPath == null) { - errorLog("No associated application for Health Channel:" + channelPath); - return null; + if (chan != null) { + config = chan.mConfig; } else { - for (Entry<BluetoothHealthAppConfiguration, String> e : - mHealthAppConfigs.entrySet()) { - if (e.getValue().equals(configPath)) { - config = e.getKey(); + String configPath = mBluetoothService.getChannelApplicationNative(channelPath); + if (configPath == null) { + errorLog("Config path is null for application"); + } else { + for (Entry<BluetoothHealthAppConfiguration, String> e : + mHealthAppConfigs.entrySet()) { + if (e.getValue().equals(configPath)) { + config = e.getKey(); + } } - } - if (config == null) { - errorLog("No associated application for application path:" + configPath); - return null; + if (config == null) errorLog("No associated application for path:" + configPath); } } return config; @@ -375,78 +417,83 @@ final class BluetoothHealthProfileHandler { if (address == null) return; BluetoothDevice device = adapter.getRemoteDevice(address); - - BluetoothHealthAppConfiguration config = findHealthApplication(device, - channelPath); + BluetoothHealthAppConfiguration config; int state, prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; ParcelFileDescriptor fd; HealthChannel channel; + config = findHealthApplication(device, channelPath); - if (config != null) { - if (exists) { - fd = mBluetoothService.getChannelFdNative(channelPath); - - if (fd == null) { - errorLog("Error obtaining fd for channel:" + channelPath); - return; - } - - boolean mainChannel = - getMainChannel(device, config) == null ? false : true; - if (!mainChannel) { - String mainChannelPath = - mBluetoothService.getMainChannelNative(devicePath); - if (mainChannelPath == null) { - errorLog("Main Channel Path is null for devicePath:" + devicePath); - return; - } - if (mainChannelPath.equals(channelPath)) mainChannel = true; - } - - channel = findConnectingChannel(device, config); - if (channel != null) { - channel.mChannelFd = fd; - channel.mMainChannel = mainChannel; - channel.mChannelPath = channelPath; - prevState = channel.mState; - } else { - channel = new HealthChannel(device, config, fd, mainChannel, - channelPath); - mHealthChannels.add(channel); - prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; - } - state = BluetoothHealth.STATE_CHANNEL_CONNECTED; - } else { - channel = findChannelByPath(device, config, channelPath); - if (channel == null) { - errorLog("Channel not found:" + config + ":" + channelPath); - return; - } - - fd = channel.mChannelFd; - // CLOSE FD - mBluetoothService.releaseChannelFdNative(channel.mChannelPath); - mHealthChannels.remove(channel); - - prevState = channel.mState; - state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; - } - channel.mState = state; - callHealthChannelCallback(config, device, prevState, state, fd); + if (exists) { + fd = mBluetoothService.getChannelFdNative(channelPath); + if (fd == null) { + errorLog("Error obtaining fd for channel:" + channelPath); + return; + } + boolean mainChannel = + getMainChannel(device, config) == null ? false : true; + if (!mainChannel) { + String mainChannelPath = + mBluetoothService.getMainChannelNative(devicePath); + if (mainChannelPath == null) { + errorLog("Main Channel Path is null for devicePath:" + devicePath); + return; + } + if (mainChannelPath.equals(channelPath)) mainChannel = true; + } + channel = findConnectingChannel(device, config); + if (channel != null) { + channel.mChannelFd = fd; + channel.mMainChannel = mainChannel; + channel.mChannelPath = channelPath; + prevState = channel.mState; + } else { + channel = new HealthChannel(device, config, fd, mainChannel, + channelPath); + mHealthChannels.add(channel); + prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; + } + state = BluetoothHealth.STATE_CHANNEL_CONNECTED; + } else { + channel = findChannelByPath(device, channelPath); + if (channel == null) { + errorLog("Channel not found:" + config + ":" + channelPath); + return; + } + mHealthChannels.remove(channel); + + channel.mChannelFd = null; + prevState = channel.mState; + state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; } + channel.mState = state; + callHealthChannelCallback(config, device, prevState, state, channel.mChannelFd, + channel.hashCode()); } private void callHealthChannelCallback(BluetoothHealthAppConfiguration config, - BluetoothDevice device, int prevState, int state, ParcelFileDescriptor fd) { + BluetoothDevice device, int prevState, int state, ParcelFileDescriptor fd, int id) { broadcastHealthDeviceStateChange(device, prevState, state); debugLog("Health Device Callback: " + device + " State Change: " + prevState + "->" + state); + + ParcelFileDescriptor dupedFd = null; + if (fd != null) { + try { + dupedFd = fd.dup(); + } catch (IOException e) { + dupedFd = null; + errorLog("Exception while duping: " + e); + } + } + IBluetoothHealthCallback callback = mCallbacks.get(config); if (callback != null) { try { - callback.onHealthChannelStateChange(config, device, prevState, state, fd); - } catch (RemoteException e) {} + callback.onHealthChannelStateChange(config, device, prevState, state, dupedFd, id); + } catch (RemoteException e) { + errorLog("Remote Exception:" + e); + } } } @@ -458,7 +505,9 @@ final class BluetoothHealthProfileHandler { if (callback != null) { try { callback.onHealthAppConfigurationStatusChange(config, status); - } catch (RemoteException e) {} + } catch (RemoteException e) { + errorLog("Remote Exception:" + e); + } } } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 55a0624..03180ca 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -95,7 +95,6 @@ public class BluetoothService extends IBluetooth.Stub { private boolean mIsAirplaneSensitive; private boolean mIsAirplaneToggleable; private BluetoothAdapterStateMachine mBluetoothState; - private boolean mRestart = false; // need to call enable() after disable() private int[] mAdapterSdpHandles; private ParcelUuid[] mAdapterUuids; @@ -429,11 +428,6 @@ public class BluetoothService extends IBluetooth.Stub { } finally { Binder.restoreCallingIdentity(ident); } - - if (mRestart) { - mRestart = false; - enable(); - } } /** @@ -456,10 +450,6 @@ public class BluetoothService extends IBluetooth.Stub { // the adapter property could be changed before event loop is stoped, clear it again mAdapterProperties.clear(); disableNative(); - if (mRestart) { - mRestart = false; - enable(); - } } /** Bring up BT and persist BT on in settings */ @@ -500,17 +490,6 @@ public class BluetoothService extends IBluetooth.Stub { return true; } - /** Forcibly restart Bluetooth if it is on */ - /* package */ synchronized void restart() { - if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) { - return; - } - mRestart = true; - if (!disable(false)) { - mRestart = false; - } - } - private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -2243,11 +2222,11 @@ public class BluetoothService extends IBluetooth.Stub { } public boolean disconnectChannel(BluetoothDevice device, - BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { + BluetoothHealthAppConfiguration config, int id) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); synchronized (mBluetoothHealthProfileHandler) { - return mBluetoothHealthProfileHandler.disconnectChannel(device, config, fd); + return mBluetoothHealthProfileHandler.disconnectChannel(device, config, id); } } diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java index 625d869..afd9892 100644 --- a/core/java/android/text/TextPaint.java +++ b/core/java/android/text/TextPaint.java @@ -72,8 +72,15 @@ public class TextPaint extends Paint { linkColor = tp.linkColor; drawableState = tp.drawableState; density = tp.density; - underlineColors = tp.underlineColors; - underlineThicknesses = tp.underlineThicknesses; + + if (tp.underlineColors != null) { + if (underlineColors == null || underlineColors.length < tp.underlineCount) { + underlineColors = new int[tp.underlineCount]; + underlineThicknesses = new float[tp.underlineCount]; + } + System.arraycopy(tp.underlineColors, 0, underlineColors, 0, tp.underlineCount); + System.arraycopy(tp.underlineThicknesses, 0, underlineThicknesses, 0, tp.underlineCount); + } underlineCount = tp.underlineCount; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fd60813..4a9c5bd 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9995,6 +9995,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } final HardwareCanvas canvas = mDisplayList.start(); + int restoreCount = 0; try { int width = mRight - mLeft; int height = mBottom - mTop; @@ -10004,6 +10005,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal canvas.onPreDraw(null); computeScroll(); + + restoreCount = canvas.save(); canvas.translate(-mScrollX, -mScrollY); mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID; mPrivateFlags &= ~DIRTY_MASK; @@ -10015,6 +10018,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal draw(canvas); } } finally { + canvas.restoreToCount(restoreCount); canvas.onPostDraw(); mDisplayList.end(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b3dd071..fb3f6e8 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4543,7 +4543,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, predicate.init(accessibilityId); View root = ViewRootImpl.this.mView; View target = root.findViewByPredicate(predicate); - if (target != null && target.isShown()) { + if (target != null && target.getVisibility() == View.VISIBLE) { info = target.createAccessibilityNodeInfo(); } } finally { @@ -4586,7 +4586,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, try { View root = ViewRootImpl.this.mView; View target = root.findViewById(viewId); - if (target != null && target.isShown()) { + if (target != null && target.getVisibility() == View.VISIBLE) { info = target.createAccessibilityNodeInfo(); } } finally { @@ -4637,14 +4637,14 @@ public final class ViewRootImpl extends Handler implements ViewParent, ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList; foundViews.clear(); - View root; + View root = null; if (accessibilityViewId != View.NO_ID) { root = findViewByAccessibilityId(accessibilityViewId); } else { root = ViewRootImpl.this.mView; } - if (root == null || !root.isShown()) { + if (root == null || root.getVisibility() != View.VISIBLE) { return; } @@ -4659,7 +4659,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, final int viewCount = foundViews.size(); for (int i = 0; i < viewCount; i++) { View foundView = foundViews.get(i); - if (foundView.isShown()) { + if (foundView.getVisibility() == View.VISIBLE) { infos.add(foundView.createAccessibilityNodeInfo()); } } @@ -4732,7 +4732,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, private boolean performActionFocus(int accessibilityId) { View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { + if (target == null || target.getVisibility() != View.VISIBLE) { return false; } // Get out of touch mode since accessibility wants to move focus around. @@ -4742,7 +4742,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, private boolean performActionClearFocus(int accessibilityId) { View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { + if (target == null || target.getVisibility() != View.VISIBLE) { return false; } if (!target.isFocused()) { @@ -4754,7 +4754,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, private boolean performActionSelect(int accessibilityId) { View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { + if (target == null || target.getVisibility() != View.VISIBLE) { return false; } if (target.isSelected()) { @@ -4766,7 +4766,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, private boolean performActionClearSelection(int accessibilityId) { View target = findViewByAccessibilityId(accessibilityId); - if (target == null) { + if (target == null || target.getVisibility() != View.VISIBLE) { return false; } if (!target.isSelected()) { @@ -4783,18 +4783,21 @@ public final class ViewRootImpl extends Handler implements ViewParent, } mFindByAccessibilityIdPredicate.init(accessibilityId); View foundView = root.findViewByPredicate(mFindByAccessibilityIdPredicate); - return (foundView != null && foundView.isShown()) ? foundView : null; + if (foundView == null || foundView.getVisibility() != View.VISIBLE) { + return null; + } + return foundView; } private final class FindByAccessibilitytIdPredicate implements Predicate<View> { - public int mSerchedId; + public int mSearchedId; public void init(int searchedId) { - mSerchedId = searchedId; + mSearchedId = searchedId; } public boolean apply(View view) { - return (view.getAccessibilityViewId() == mSerchedId); + return (view.getAccessibilityViewId() == mSearchedId); } } } diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java index 1b9a25e..97dc291 100644 --- a/core/java/android/webkit/HTML5VideoInline.java +++ b/core/java/android/webkit/HTML5VideoInline.java @@ -7,6 +7,7 @@ import android.webkit.HTML5VideoView; import android.webkit.HTML5VideoViewProxy; import android.view.Surface; import android.opengl.GLES20; +import android.os.PowerManager; /** * @hide This is only used by the browser @@ -51,6 +52,7 @@ public class HTML5VideoInline extends HTML5VideoView{ public void prepareDataAndDisplayMode(HTML5VideoViewProxy proxy) { super.prepareDataAndDisplayMode(proxy); setFrameAvailableListener(proxy); + mPlayer.setWakeMode(proxy.getContext(), PowerManager.FULL_WAKE_LOCK); } // Pause the play and update the play/pause button diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index b945038..2d10bbe 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -886,9 +886,11 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); } - // We first get a chance to populate the event. - onPopulateAccessibilityEvent(event); - + View selectedView = getSelectedView(); + if (selectedView != null && selectedView.getVisibility() == VISIBLE) { + // We first get a chance to populate the event. + onPopulateAccessibilityEvent(event); + } return false; } @@ -896,10 +898,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { public void onPopulateAccessibilityEvent(AccessibilityEvent event) { // We send selection events only from AdapterView to avoid // generation of such event for each child. - View selectedView = getSelectedView(); - if (selectedView != null) { - selectedView.dispatchPopulateAccessibilityEvent(event); - } + getSelectedView().dispatchPopulateAccessibilityEvent(event); } @Override diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 0b0b812..398a7eb 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -465,23 +465,42 @@ public class FrameLayout extends ViewGroup { } /** - * Determines whether to measure all children or just those in - * the VISIBLE or INVISIBLE state when measuring. Defaults to false. + * Sets whether to consider all children, or just those in + * the VISIBLE or INVISIBLE state, when measuring. Defaults to false. + * * @param measureAll true to consider children marked GONE, false otherwise. * Default value is false. - * + * * @attr ref android.R.styleable#FrameLayout_measureAllChildren */ @android.view.RemotableViewMethod public void setMeasureAllChildren(boolean measureAll) { mMeasureAllChildren = measureAll; } - + /** - * Determines whether to measure all children or just those in - * the VISIBLE or INVISIBLE state when measuring. + * Determines whether all children, or just those in the VISIBLE or + * INVISIBLE state, are considered when measuring. + * + * @return Whether all children are considered when measuring. + * + * @deprecated This method is deprecated in favor of + * {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was + * renamed for consistency with + * {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}. */ + @Deprecated public boolean getConsiderGoneChildrenWhenMeasuring() { + return getMeasureAllChildren(); + } + + /** + * Determines whether all children, or just those in the VISIBLE or + * INVISIBLE state, are considered when measuring. + * + * @return Whether all children are considered when measuring. + */ + public boolean getMeasureAllChildren() { return mMeasureAllChildren; } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index a5cf62e..6edfd59 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -961,7 +961,8 @@ public class RelativeLayout extends ViewGroup { } for (View view : mTopToBottomLeftToRightSet) { - if (view.dispatchPopulateAccessibilityEvent(event)) { + if (view.getVisibility() == View.VISIBLE + && view.dispatchPopulateAccessibilityEvent(event)) { mTopToBottomLeftToRightSet.clear(); return true; } diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 9afb625..191c4ca 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -405,7 +405,10 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { onPopulateAccessibilityEvent(event); // Dispatch only to the selected tab. if (mSelectedTab != -1) { - return getChildTabViewAt(mSelectedTab).dispatchPopulateAccessibilityEvent(event); + View tabView = getChildTabViewAt(mSelectedTab); + if (tabView != null && tabView.getVisibility() == VISIBLE) { + return tabView.dispatchPopulateAccessibilityEvent(event); + } } return false; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b93c140..d62cf47 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5426,8 +5426,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mMovement != null && mText instanceof Editable && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); + viewClicked(imm); if (imm != null) { - imm.viewClicked(this); imm.showSoftInput(this, 0); } } @@ -5740,7 +5740,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener MetaKeyKeyListener.stopSelecting(this, sp); } } - + /** * @hide */ @@ -5748,10 +5748,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mInputMethodState != null) { mInputMethodState.mExtracting = req; } - // This stops a possible text selection mode. Maybe not intended. + // This would stop a possible selection mode, but no such mode is started in case + // extracted mode will start. Some text is selected though, and will trigger an action mode + // in the extracted view. hideControllers(); } - + /** * Called by the framework in response to a text completion from * the current input method, provided by it calling @@ -8340,9 +8342,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (touchIsFinished && (isTextEditable() || mTextIsSelectable)) { // Show the IME, except when selecting in read-only text. final InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.viewClicked(this); - } + viewClicked(imm); if (!mTextIsSelectable) { handled |= imm != null && imm.showSoftInput(this, 0); } @@ -9222,21 +9222,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean performLongClick() { + boolean handled = false; + boolean vibrate = true; + if (super.performLongClick()) { mDiscardNextActionUp = true; - return true; + handled = true; } - boolean handled = false; - // Long press in empty space moves cursor and shows the Paste affordance if available. - if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) && + if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) && mInsertionControllerEnabled) { final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); stopSelectionActionMode(); Selection.setSelection((Spannable) mText, offset); getInsertionController().showWithActionPopup(); handled = true; + vibrate = false; } if (!handled && mSelectionActionMode != null) { @@ -9258,10 +9260,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Start a new selection - handled |= !handled && startSelectionActionMode(); + if (!handled) { + handled = startSelectionActionMode(); + } - if (handled) { + if (vibrate) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } + + if (handled) { mDiscardNextActionUp = true; } @@ -10009,14 +10016,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = startActionMode(actionModeCallback); - final boolean selectionStarted = mSelectionActionMode != null; + final InputMethodManager imm = InputMethodManager.peekInstance(); + boolean extractedTextModeWillBeStartedFullScreen = !(this instanceof ExtractEditText) && + imm != null && imm.isFullscreenMode(); + + // Do not start the action mode when extracted text will show up full screen, thus + // immediately hiding the newly created action bar, which would be visually distracting. + if (!extractedTextModeWillBeStartedFullScreen) { + ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); + mSelectionActionMode = startActionMode(actionModeCallback); + } + final boolean selectionStarted = mSelectionActionMode != null || + extractedTextModeWillBeStartedFullScreen; - if (selectionStarted && !mTextIsSelectable) { + if (selectionStarted && !mTextIsSelectable && imm != null) { // Show the IME to be able to replace text, except when selecting non editable text. - final InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) imm.showSoftInput(this, 0, null); + imm.showSoftInput(this, 0, null); } return selectionStarted; @@ -11304,6 +11319,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mResolvedDrawables = false; } + /** + * @hide + */ + protected void viewClicked(InputMethodManager imm) { + if (imm != null) { + imm.viewClicked(this); + } + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; @@ -11326,7 +11350,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final TextPaint mTextPaint; private boolean mUserSetTextScaleX; private final Paint mHighlightPaint; - private int mHighlightColor = 0x4C33B5E5; + private int mHighlightColor = 0x6633B5E5; /** * This is temporarily visible to fix bug 3085564 in webView. Do not rely on * this field being protected. Will be restored as private when lineHeight |