Merge "Make the SyncManager have wakelocks per account, not per account type, otherwise the parallel sync behavior of gmail can cause the first account to finish to release the wakelock that is used for the other account, which will result in the device going to sleep before the sync is complete."
diff --git a/api/current.txt b/api/current.txt
index cc6cce9..d113e90 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2452,6 +2452,8 @@
method public abstract void setLogo(android.graphics.drawable.Drawable);
method public abstract void setNavigationMode(int);
method public abstract void setSelectedNavigationItem(int);
+ method public void setSplitBackgroundDrawable(android.graphics.drawable.Drawable);
+ method public void setStackedBackgroundDrawable(android.graphics.drawable.Drawable);
method public abstract void setSubtitle(java.lang.CharSequence);
method public abstract void setSubtitle(int);
method public abstract void setTitle(java.lang.CharSequence);
@@ -4526,6 +4528,44 @@
field public static final java.lang.String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid";
}
+ public final class BluetoothHealth implements android.bluetooth.BluetoothProfile {
+ method public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method public boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
+ method public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
+ field public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
+ field public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
+ field public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
+ field public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
+ field public static final int CHANNEL_TYPE_RELIABLE = 10; // 0xa
+ field public static final int CHANNEL_TYPE_STREAMING = 11; // 0xb
+ field public static final int SINK_ROLE = 2; // 0x2
+ field public static final int SOURCE_ROLE = 1; // 0x1
+ field public static final int STATE_CHANNEL_CONNECTED = 2; // 0x2
+ field public static final int STATE_CHANNEL_CONNECTING = 1; // 0x1
+ field public static final int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
+ field public static final int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
+ }
+
+ public final class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getDataType();
+ method public java.lang.String getName();
+ method public int getRole();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public abstract class BluetoothHealthCallback {
+ ctor public BluetoothHealthCallback();
+ method public void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
+ method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
+ }
+
public abstract interface BluetoothProfile {
method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public abstract int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -6214,6 +6254,7 @@
field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi";
+ field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final int GET_ACTIVITIES = 1; // 0x1
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
@@ -8896,7 +8937,6 @@
method public int getMinimumWidth();
method public abstract int getOpacity();
method public boolean getPadding(android.graphics.Rect);
- method public int getResolvedLayoutDirectionSelf();
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -8931,10 +8971,6 @@
method public abstract void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
}
- public static abstract interface Drawable.Callback2 implements android.graphics.drawable.Drawable.Callback {
- method public abstract int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
- }
-
public static abstract class Drawable.ConstantState {
ctor public Drawable.ConstantState();
method public abstract int getChangingConfigurations();
@@ -9326,6 +9362,7 @@
method public boolean isAutoExposureLockSupported();
method public boolean isAutoWhiteBalanceLockSupported();
method public boolean isSmoothZoomSupported();
+ method public boolean isVideoSnapshotSupported();
method public boolean isZoomSupported();
method public void remove(java.lang.String);
method public void removeGpsData();
@@ -16663,6 +16700,7 @@
field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact_entity";
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DATA_ID = "data_id";
+ field public static final android.net.Uri PROFILE_CONTENT_URI;
}
public static final class ContactsContract.Settings implements android.provider.ContactsContract.SettingsColumns {
@@ -16675,6 +16713,7 @@
field public static final java.lang.String ACCOUNT_NAME = "account_name";
field public static final java.lang.String ACCOUNT_TYPE = "account_type";
field public static final java.lang.String ANY_UNSYNCED = "any_unsynced";
+ field public static final java.lang.String DATA_SET = "data_set";
field public static final java.lang.String SHOULD_SYNC = "should_sync";
field public static final java.lang.String UNGROUPED_COUNT = "summ_count";
field public static final java.lang.String UNGROUPED_VISIBLE = "ungrouped_visible";
@@ -22665,7 +22704,7 @@
method public void recycle();
}
- public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback2 android.view.KeyEvent.Callback {
+ public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
ctor public View(android.content.Context);
ctor public View(android.content.Context, android.util.AttributeSet);
ctor public View(android.content.Context, android.util.AttributeSet, int);
@@ -22682,7 +22721,6 @@
method public void buildDrawingCache();
method public void buildDrawingCache(boolean);
method public void buildLayer();
- method protected boolean canResolveLayoutDirection();
method public boolean canScrollHorizontally(int);
method public boolean canScrollVertically(int);
method public void cancelLongPress();
@@ -22794,7 +22832,6 @@
method public final android.view.ViewParent getParent();
method public float getPivotX();
method public float getPivotY();
- method public int getResolvedLayoutDirection(android.graphics.drawable.Drawable);
method public android.content.res.Resources getResources();
method public final int getRight();
method protected float getRightFadingEdgeStrength();
@@ -22859,7 +22896,6 @@
method public boolean isHovered();
method public boolean isInEditMode();
method public boolean isInTouchMode();
- method protected static boolean isLayoutDirectionRtl(java.util.Locale);
method public boolean isLayoutRequested();
method public boolean isLongClickable();
method public boolean isOpaque();
@@ -22945,10 +22981,8 @@
method public void requestLayout();
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
- method protected void resetResolvedTextDirection();
method public static int resolveSize(int, int);
method public static int resolveSizeAndState(int, int, int);
- method protected void resolveTextDirection();
method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
@@ -23049,7 +23083,6 @@
method public boolean willNotCacheDrawing();
method public boolean willNotDraw();
field public static android.util.Property ALPHA;
- field protected static int DEFAULT_TEXT_DIRECTION;
field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -23378,6 +23411,7 @@
method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method protected void resetResolvedLayoutDirection();
+ method protected void resetResolvedTextDirection();
method public void scheduleLayoutAnimation();
method public void setAddStatesFromChildren(boolean);
method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -25923,8 +25957,9 @@
ctor public FrameLayout(android.content.Context);
ctor public FrameLayout(android.content.Context, android.util.AttributeSet);
ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int);
- method public boolean getConsiderGoneChildrenWhenMeasuring();
+ method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
method public android.graphics.drawable.Drawable getForeground();
+ method public boolean getMeasureAllChildren();
method protected void onLayout(boolean, int, int, int, int);
method public void setForeground(android.graphics.drawable.Drawable);
method public void setForegroundGravity(int);
@@ -27175,6 +27210,7 @@
method protected void resetResolvedDrawables();
method protected void resetResolvedLayoutDirection();
method protected void resolveDrawables();
+ method protected void resolveTextDirection();
method public void setAllCaps(boolean);
method public final void setAutoLinkMask(int);
method public void setCompoundDrawablePadding(int);
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 9e2b833..355b1fc 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -793,7 +793,9 @@
* @hide
*/
public void startChangingAnimations() {
- for (Animator anim : currentChangingAnimations.values()) {
+ LinkedHashMap<View, Animator> currentAnimCopy =
+ (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
+ for (Animator anim : currentAnimCopy.values()) {
if (anim instanceof ObjectAnimator) {
((ObjectAnimator) anim).setCurrentPlayTime(0);
}
@@ -802,6 +804,23 @@
}
/**
+ * Ends the animations that are set up for a CHANGING transition. This is a variant of
+ * startChangingAnimations() which is called when the window the transition is playing in
+ * is not visible. We need to make sure the animations put their targets in their end states
+ * and that the transition finishes to remove any mid-process state (such as isRunning()).
+ *
+ * @hide
+ */
+ public void endChangingAnimations() {
+ LinkedHashMap<View, Animator> currentAnimCopy =
+ (LinkedHashMap<View, Animator>) currentChangingAnimations.clone();
+ for (Animator anim : currentAnimCopy.values()) {
+ anim.start();
+ anim.end();
+ }
+ }
+
+ /**
* Returns true if animations are running which animate layout-related properties. This
* essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations
* are running, since these animations operate on layout-related properties.
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 46dc5ff..0d4a067 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -402,13 +402,33 @@
public abstract void setDisplayShowCustomEnabled(boolean showCustom);
/**
- * Set the ActionBar's background.
+ * Set the ActionBar's background. This will be used for the primary
+ * action bar.
*
* @param d Background drawable
+ * @see #setStackedBackgroundDrawable(Drawable)
+ * @see #setSplitBackgroundDrawable(Drawable)
*/
public abstract void setBackgroundDrawable(Drawable d);
/**
+ * Set the ActionBar's stacked background. This will appear
+ * in the second row/stacked bar on some devices and configurations.
+ *
+ * @param d Background drawable for the stacked row
+ */
+ public void setStackedBackgroundDrawable(Drawable d) { }
+
+ /**
+ * Set the ActionBar's split background. This will appear in
+ * the split action bar containing menu-provided action buttons
+ * on some devices and configurations.
+ *
+ * @param d Background drawable for the split bar
+ */
+ public void setSplitBackgroundDrawable(Drawable d) { }
+
+ /**
* @return The current custom view.
*/
public abstract View getCustomView();
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 @@
* 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 @@
* 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 @@
* 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 @@
* {@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 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 @@
dest.writeInt(importanceReasonCode);
dest.writeInt(importanceReasonPid);
ComponentName.writeToParcel(importanceReasonComponent, dest);
+ dest.writeInt(importanceReasonImportance);
}
public void readFromParcel(Parcel source) {
@@ -1293,6 +1300,7 @@
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 @@
* <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 @@
*
* @param config The health app configuration
* @return Success or failure.
- * @hide
*/
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
boolean result = false;
@@ -222,16 +236,15 @@
* @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 @@
*
* <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 @@
}
/**
- * 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 @@
@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 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 @@
* 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 @@
*
* @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 @@
* @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 @@
mChannelType + "]";
}
- @Override
public int describeContents() {
return 0;
}
@@ -132,6 +132,7 @@
* @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 @@
}
};
- @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.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 @@
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 @@
{
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 @@
* 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 b72a3d2..9678d48 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -192,10 +192,10 @@
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/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5c641f1..b4e3988 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1021,6 +1021,13 @@
public static final String FEATURE_WIFI = "android.hardware.wifi";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device supports Wi-Fi Direct networking.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
+
+ /**
* Action to external storage service to clean out removed apps.
* @hide
*/
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 63f2244..58f7869 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3233,7 +3233,6 @@
* captured pictures.
*
* @return true if video snapshot is supported.
- * @hide
*/
public boolean isVideoSnapshotSupported() {
String str = get(KEY_VIDEO_SNAPSHOT_SUPPORTED);
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 0411b5c..784bcc5 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -154,16 +154,16 @@
* All values are in micro-Tesla (uT) and measure the ambient magnetic field
* in the X, Y and Z axis.
*
- * <h4>{@link android.hardware.Sensor#TYPE_GYROSCOPE Sensor.TYPE_GYROSCOPE}:</h4>
- * All values are in radians/second and measure the rate of rotation
- * around the X, Y and Z axis. The coordinate system is the same as is
- * used for the acceleration sensor. Rotation is positive in the counter-clockwise
- * direction. That is, an observer looking from some positive location on the x, y.
- * or z axis at a device positioned on the origin would report positive rotation
- * if the device appeared to be rotating counter clockwise. Note that this is the
- * standard mathematical definition of positive rotation and does not agree with the
- * definition of roll given earlier.
- *
+ * <h4>{@link android.hardware.Sensor#TYPE_GYROSCOPE Sensor.TYPE_GYROSCOPE}:
+ * </h4> All values are in radians/second and measure the rate of rotation
+ * around the device's local X, Y and Z axis. The coordinate system is the
+ * same as is used for the acceleration sensor. Rotation is positive in the
+ * counter-clockwise direction. That is, an observer looking from some
+ * positive location on the x, y or z axis at a device positioned on the
+ * origin would report positive rotation if the device appeared to be
+ * rotating counter clockwise. Note that this is the standard mathematical
+ * definition of positive rotation and does not agree with the definition of
+ * roll given earlier.
* <ul>
* <p>
* values[0]: Angular speed around the x-axis
@@ -176,28 +176,61 @@
* </p>
* </ul>
* <p>
- * Typically the output of the gyroscope is integrated over time to calculate
- * an angle, for example:
+ * Typically the output of the gyroscope is integrated over time to
+ * calculate a rotation describing the change of angles over the timestep,
+ * for example:
* </p>
+ *
* <pre class="prettyprint">
* private static final float NS2S = 1.0f / 1000000000.0f;
+ * private final float[] deltaRotationVector = new float[4]();
* private float timestamp;
- * public void onSensorChanged(SensorEvent event)
- * {
+ *
+ * public void onSensorChanged(SensorEvent event) {
+ * // This timestep's delta rotation to be multiplied by the current rotation
+ * // after computing it from the gyro sample data.
* if (timestamp != 0) {
* final float dT = (event.timestamp - timestamp) * NS2S;
- * angle[0] += event.values[0] * dT;
- * angle[1] += event.values[1] * dT;
- * angle[2] += event.values[2] * dT;
+ * // Axis of the rotation sample, not normalized yet.
+ * float axisX = event.values[0];
+ * float axisY = event.values[1];
+ * float axisZ = event.values[2];
+ *
+ * // Calculate the angular speed of the sample
+ * float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
+ *
+ * // Normalize the rotation vector if it's big enough to get the axis
+ * if (omegaMagnitude > EPSILON) {
+ * axisX /= omegaMagnitude;
+ * axisY /= omegaMagnitude;
+ * axisZ /= omegaMagnitude;
+ * }
+ *
+ * // Integrate around this axis with the angular speed by the timestep
+ * // in order to get a delta rotation from this sample over the timestep
+ * // We will convert this axis-angle representation of the delta rotation
+ * // into a quaternion before turning it into the rotation matrix.
+ * float thetaOverTwo = omegaMagnitude * dT / 2.0f;
+ * float sinThetaOverTwo = sin(thetaOverTwo);
+ * float cosThetaOverTwo = cos(thetaOverTwo);
+ * deltaRotationVector[0] = sinThetaOverTwo * axisX;
+ * deltaRotationVector[1] = sinThetaOverTwo * axisY;
+ * deltaRotationVector[2] = sinThetaOverTwo * axisZ;
+ * deltaRotationVector[3] = cosThetaOverTwo;
* }
* timestamp = event.timestamp;
+ * float[] deltaRotationMatrix = new float[9];
+ * SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
+ * // User code should concatenate the delta rotation we computed with the current rotation
+ * // in order to get the updated rotation.
+ * // rotationCurrent = rotationCurrent * deltaRotationMatrix;
* }
* </pre>
- *
- * <p>In practice, the gyroscope noise and offset will introduce some errors which need
- * to be compensated for. This is usually done using the information from other
- * sensors, but is beyond the scope of this document.</p>
- *
+ * <p>
+ * In practice, the gyroscope noise and offset will introduce some errors
+ * which need to be compensated for. This is usually done using the
+ * information from other sensors, but is beyond the scope of this document.
+ * </p>
* <h4>{@link android.hardware.Sensor#TYPE_LIGHT Sensor.TYPE_LIGHT}:</h4>
* <ul>
* <p>
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 @@
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 @@
@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/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 370e22a..7d3cd92 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -618,7 +618,7 @@
mTheme = Resources.selectSystemTheme(mTheme,
getApplicationInfo().targetSdkVersion,
android.R.style.Theme_InputMethod,
- android.R.style.Theme_Holo,
+ android.R.style.Theme_Holo_InputMethod,
android.R.style.Theme_DeviceDefault_InputMethod);
super.setTheme(mTheme);
super.onCreate();
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 @@
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 @@
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.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.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 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 @@
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 @@
* @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 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 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
// 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 @@
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 @@
} 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 @@
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.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 @@
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 @@
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 @@
}
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 @@
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 @@
};
private BluetoothHealthProfileHandler(Context context, BluetoothService service) {
- mContext = context;
mBluetoothService = service;
mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>();
mHealthChannels = new ArrayList<HealthChannel>();
@@ -205,7 +227,7 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 (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);
- 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);
+ 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 @@
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 @@
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 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
-
- if (mRestart) {
- mRestart = false;
- enable();
- }
}
/**
@@ -456,10 +450,6 @@
// 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 @@
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 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 @@
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 ba23218..4a9c5bd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -636,7 +636,8 @@
*
* @see android.view.ViewGroup
*/
-public class View implements Drawable.Callback2, KeyEvent.Callback, AccessibilityEventSource {
+public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Callback,
+ AccessibilityEventSource {
private static final boolean DBG = false;
/**
@@ -2583,6 +2584,8 @@
/**
* Default text direction is inherited
+ *
+ * @hide
*/
protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
@@ -9294,6 +9297,11 @@
recomputePadding();
}
+ /**
+ * Return true if layout direction resolution can be done
+ *
+ * @hide
+ */
protected boolean canResolveLayoutDirection() {
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
@@ -9322,6 +9330,8 @@
*
* @param locale Locale to check
* @return true if a Locale is corresponding to a RTL script.
+ *
+ * @hide
*/
protected static boolean isLayoutDirectionRtl(Locale locale) {
return (LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE ==
@@ -9985,6 +9995,7 @@
}
final HardwareCanvas canvas = mDisplayList.start();
+ int restoreCount = 0;
try {
int width = mRight - mLeft;
int height = mBottom - mTop;
@@ -9994,6 +10005,8 @@
canvas.onPreDraw(null);
computeScroll();
+
+ restoreCount = canvas.save();
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID;
mPrivateFlags &= ~DIRTY_MASK;
@@ -10005,6 +10018,7 @@
draw(canvas);
}
} finally {
+ canvas.restoreToCount(restoreCount);
canvas.onPostDraw();
mDisplayList.end();
@@ -13240,7 +13254,6 @@
* {@link #TEXT_DIRECTION_INHERIT},
* {@link #TEXT_DIRECTION_FIRST_STRONG}
* {@link #TEXT_DIRECTION_ANY_RTL},
- * {@link #TEXT_DIRECTION_CHAR_COUNT},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
*
@@ -13258,7 +13271,6 @@
* {@link #TEXT_DIRECTION_INHERIT},
* {@link #TEXT_DIRECTION_FIRST_STRONG}
* {@link #TEXT_DIRECTION_ANY_RTL},
- * {@link #TEXT_DIRECTION_CHAR_COUNT},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
*
@@ -13279,7 +13291,6 @@
*
* {@link #TEXT_DIRECTION_FIRST_STRONG}
* {@link #TEXT_DIRECTION_ANY_RTL},
- * {@link #TEXT_DIRECTION_CHAR_COUNT},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
*
@@ -13294,6 +13305,8 @@
/**
* Resolve the text direction.
+ *
+ * @hide
*/
protected void resolveTextDirection() {
if (mTextDirection != TEXT_DIRECTION_INHERIT) {
@@ -13309,6 +13322,8 @@
/**
* Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection().
+ *
+ * @hide
*/
protected void resetResolvedTextDirection() {
mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2e2b3d6..fb3f6e8 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1575,13 +1575,13 @@
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw() ||
viewVisibility != View.VISIBLE;
- if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
- for (int i = 0; i < mPendingTransitions.size(); ++i) {
- mPendingTransitions.get(i).startChangingAnimations();
- }
- mPendingTransitions.clear();
- }
if (!cancelDraw && !newSurface) {
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).startChangingAnimations();
+ }
+ mPendingTransitions.clear();
+ }
mFullRedrawNeeded = false;
final long drawStartTime;
@@ -1619,7 +1619,13 @@
}
}
} else {
-
+ // End any pending transitions on this non-visible window
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).endChangingAnimations();
+ }
+ mPendingTransitions.clear();
+ }
// We were supposed to report when we are done drawing. Since we canceled the
// draw, remember it here.
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
@@ -4537,7 +4543,7 @@
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 {
@@ -4580,7 +4586,7 @@
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 {
@@ -4631,14 +4637,14 @@
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;
}
@@ -4653,7 +4659,7 @@
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());
}
}
@@ -4726,7 +4732,7 @@
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.
@@ -4736,7 +4742,7 @@
private boolean performActionClearFocus(int accessibilityId) {
View target = findViewByAccessibilityId(accessibilityId);
- if (target == null) {
+ if (target == null || target.getVisibility() != View.VISIBLE) {
return false;
}
if (!target.isFocused()) {
@@ -4748,7 +4754,7 @@
private boolean performActionSelect(int accessibilityId) {
View target = findViewByAccessibilityId(accessibilityId);
- if (target == null) {
+ if (target == null || target.getVisibility() != View.VISIBLE) {
return false;
}
if (target.isSelected()) {
@@ -4760,7 +4766,7 @@
private boolean performActionClearSelection(int accessibilityId) {
View target = findViewByAccessibilityId(accessibilityId);
- if (target == null) {
+ if (target == null || target.getVisibility() != View.VISIBLE) {
return false;
}
if (!target.isSelected()) {
@@ -4777,18 +4783,21 @@
}
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/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 4f2542b..58373bc 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Build;
import android.util.AttributeSet;
import android.graphics.RectF;
@@ -31,6 +32,22 @@
* If AnimationSet sets any properties that its children also set
* (for example, duration or fillBefore), the values of AnimationSet
* override the child values.
+ *
+ * <p>The way that AnimationSet inherits behavior from Animation is important to
+ * understand. Some of the Animation attributes applied to AnimationSet affect the
+ * AnimationSet itself, some are pushed down to the children, and some are ignored,
+ * as follows:
+ * <ul>
+ * <li>duration, repeatMode, fillBefore, fillAfter: These properties, when set
+ * on an AnimationSet object, will be pushed down to all child animations.</li>
+ * <li>repeatCount, fillEnabled: These properties are ignored for AnimationSet.</li>
+ * <li>startOffset, shareInterpolator: These properties apply to the AnimationSet itself.</li>
+ * </ul>
+ * Starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH},
+ * the behavior of these properties is the same in XML resources and at runtime (prior to that
+ * release, the values set in XML were ignored for AnimationSet). That is, calling
+ * <code>setDuration(500)</code> on an AnimationSet has the same effect as declaring
+ * <code>android:duration="500"</code> in an XML resource for an AnimationSet object.</p>
*/
public class AnimationSet extends Animation {
private static final int PROPERTY_FILL_AFTER_MASK = 0x1;
@@ -69,7 +86,26 @@
setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK,
a.getBoolean(com.android.internal.R.styleable.AnimationSet_shareInterpolator, true));
init();
-
+
+ if (context.getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ if (a.hasValue(com.android.internal.R.styleable.Animation_duration)) {
+ mFlags |= PROPERTY_DURATION_MASK;
+ }
+ if (a.hasValue(com.android.internal.R.styleable.Animation_fillBefore)) {
+ mFlags |= PROPERTY_FILL_BEFORE_MASK;
+ }
+ if (a.hasValue(com.android.internal.R.styleable.Animation_fillAfter)) {
+ mFlags |= PROPERTY_FILL_AFTER_MASK;
+ }
+ if (a.hasValue(com.android.internal.R.styleable.Animation_repeatMode)) {
+ mFlags |= PROPERTY_REPEAT_MODE_MASK;
+ }
+ if (a.hasValue(com.android.internal.R.styleable.Animation_startOffset)) {
+ mFlags |= PROPERTY_START_OFFSET_MASK;
+ }
+ }
+
a.recycle();
}
@@ -112,7 +148,6 @@
private void init() {
mStartTime = 0;
- mDuration = 0;
}
@Override
@@ -171,6 +206,7 @@
public void setDuration(long durationMillis) {
mFlags |= PROPERTY_DURATION_MASK;
super.setDuration(durationMillis);
+ mLastEnd = mStartOffset + mDuration;
}
/**
@@ -192,12 +228,16 @@
mFlags |= PROPERTY_CHANGE_BOUNDS_MASK;
}
- if (mAnimations.size() == 1) {
- mDuration = a.getStartOffset() + a.getDuration();
+ if ((mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK) {
mLastEnd = mStartOffset + mDuration;
} else {
- mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
- mDuration = mLastEnd - mStartOffset;
+ if (mAnimations.size() == 1) {
+ mDuration = a.getStartOffset() + a.getDuration();
+ mLastEnd = mStartOffset + mDuration;
+ } else {
+ mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
+ mDuration = mLastEnd - mStartOffset;
+ }
}
mDirty = true;
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.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 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/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5bb0ef2..7ba93da0 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ClipboardManager;
+import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
@@ -382,6 +383,39 @@
}
}
+ private static class OnTrimMemoryListener implements ComponentCallbacks2 {
+ private static OnTrimMemoryListener sInstance = null;
+
+ static void init(Context c) {
+ if (sInstance == null) {
+ sInstance = new OnTrimMemoryListener(c.getApplicationContext());
+ }
+ }
+
+ private OnTrimMemoryListener(Context c) {
+ c.registerComponentCallbacks(this);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // Ignore
+ }
+
+ @Override
+ public void onLowMemory() {
+ // Ignore
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ if (DebugFlags.WEB_VIEW) {
+ Log.d("WebView", "onTrimMemory: " + level);
+ }
+ WebView.nativeOnTrimMemory(level);
+ }
+
+ }
+
// A final CallbackProxy shared by WebViewCore and BrowserFrame.
private final CallbackProxy mCallbackProxy;
@@ -1194,6 +1228,8 @@
}
private void init() {
+ OnTrimMemoryListener.init(getContext());
+
setWillNotDraw(false);
setFocusable(true);
setFocusableInTouchMode(true);
@@ -9404,4 +9440,8 @@
native boolean nativeSetProperty(String key, String value);
native String nativeGetProperty(String key);
private native void nativeGetTextSelectionRegion(Region region);
+ /**
+ * See {@link ComponentCallbacks2} for the trim levels and descriptions
+ */
+ private static native void nativeOnTrimMemory(int level);
}
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 @@
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 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 @@
}
/**
- * 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 @@
}
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 @@
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 7f410aa..d62cf47 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4050,16 +4050,9 @@
int wid = tv.getPaddingLeft() + tv.getPaddingRight();
int ht = tv.getPaddingTop() + tv.getPaddingBottom();
- /*
- * Figure out how big the text would be if we laid it out to the
- * full width of this view minus the border.
- */
- int cap = getWidth() - wid;
- if (cap < 0) {
- cap = 200; // We must not be measured yet -- setFrame() will fix it.
- }
-
- Layout l = new StaticLayout(text, tv.getPaint(), cap,
+ int defaultWidthInPixels = (int) (DEFAULT_ERROR_POPUP_MAX_WIDTH_IN_DP *
+ getResources().getDisplayMetrics().density);
+ Layout l = new StaticLayout(text, tv.getPaint(), defaultWidthInPixels,
Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
float max = 0;
for (int i = 0; i < l.getLineCount(); i++) {
@@ -4067,7 +4060,8 @@
}
/*
- * Now set the popup size to be big enough for the text plus the border.
+ * Now set the popup size to be big enough for the text plus the border capped
+ * to DEFAULT_MAX_POPUP_WIDTH
*/
pop.setWidth(wid + (int) Math.ceil(max));
pop.setHeight(ht + l.getHeight());
@@ -5432,8 +5426,8 @@
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);
}
}
@@ -5746,7 +5740,7 @@
MetaKeyKeyListener.stopSelecting(this, sp);
}
}
-
+
/**
* @hide
*/
@@ -5754,10 +5748,12 @@
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
@@ -8346,9 +8342,7 @@
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);
}
@@ -9228,21 +9222,23 @@
@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) {
@@ -9264,10 +9260,15 @@
}
// Start a new selection
- handled |= !handled && startSelectionActionMode();
+ if (!handled) {
+ handled = startSelectionActionMode();
+ }
+
+ if (vibrate) {
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ }
if (handled) {
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
mDiscardNextActionUp = true;
}
@@ -9555,6 +9556,8 @@
public void dismiss() {
super.dismiss();
+ TextView.this.getPositionListener().removeSubscriber(SuggestionsPopupWindow.this);
+
if ((mText instanceof Editable) && mSuggestionRangeSpan != null) {
((Editable) mText).removeSpan(mSuggestionRangeSpan);
}
@@ -10013,14 +10016,22 @@
}
}
- 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();
- if (selectionStarted && !mTextIsSelectable) {
+ // 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 && 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;
@@ -11308,6 +11319,15 @@
mResolvedDrawables = false;
}
+ /**
+ * @hide
+ */
+ protected void viewClicked(InputMethodManager imm) {
+ if (imm != null) {
+ imm.viewClicked(this);
+ }
+ }
+
@ViewDebug.ExportedProperty(category = "text")
private CharSequence mText;
private CharSequence mTransformed;
@@ -11330,7 +11350,7 @@
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
@@ -11421,4 +11441,6 @@
private CorrectionHighlighter mCorrectionHighlighter;
// New state used to change background based on whether this TextView is multiline.
private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
+
+ private static final int DEFAULT_ERROR_POPUP_MAX_WIDTH_IN_DP = 240;
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 90d19fd..ccca22e 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -346,7 +346,17 @@
}
public void setBackgroundDrawable(Drawable d) {
- mContainerView.setBackgroundDrawable(d);
+ mContainerView.setPrimaryBackground(d);
+ }
+
+ public void setStackedBackgroundDrawable(Drawable d) {
+ mContainerView.setStackedBackground(d);
+ }
+
+ public void setSplitBackgroundDrawable(Drawable d) {
+ if (mSplitView != null) {
+ mSplitView.setSplitBackground(d);
+ }
}
public View getCustomView() {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ba2f5d4..3fba1be 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -58,9 +58,20 @@
private TextView mClearDefaultHint;
private PackageManager mPm;
+ private Intent makeMyIntent() {
+ Intent intent = new Intent(getIntent());
+ // The resolver activity is set to be hidden from recent tasks.
+ // we don't want this attribute to be propagated to the next activity
+ // being launched. Note that if the original Intent also had this
+ // flag set, we are now losing it. That should be a very rare case
+ // and we can live with this.
+ intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ return intent;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
- onCreate(savedInstanceState, new Intent(getIntent()),
+ onCreate(savedInstanceState, makeMyIntent(),
getResources().getText(com.android.internal.R.string.whichApplication),
null, null, true);
}
diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java
index 3ee11bd..906b5d5 100644
--- a/core/java/com/android/internal/backup/BackupConstants.java
+++ b/core/java/com/android/internal/backup/BackupConstants.java
@@ -23,4 +23,5 @@
public static final int TRANSPORT_OK = 0;
public static final int TRANSPORT_ERROR = 1;
public static final int TRANSPORT_NOT_INITIALIZED = 2;
+ public static final int AGENT_ERROR = 3;
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index 146c7ac..e6538b0 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -34,6 +34,8 @@
* MenuPresenter for list-style menus.
*/
public class ListMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
+ private static final String TAG = "ListMenuPresenter";
+
Context mContext;
LayoutInflater mInflater;
MenuBuilder mMenu;
@@ -76,7 +78,7 @@
public void initForMenu(Context context, MenuBuilder menu) {
if (mThemeRes != 0) {
mContext = new ContextThemeWrapper(context, mThemeRes);
- } else if (mContext == null) {
+ } else if (mContext != null) {
mContext = context;
}
mInflater = LayoutInflater.from(mContext);
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index fd9ee08..f95de62 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -76,6 +76,21 @@
mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
}
+ public void setPrimaryBackground(Drawable bg) {
+ mBackground = bg;
+ invalidate();
+ }
+
+ public void setStackedBackground(Drawable bg) {
+ mStackedBackground = bg;
+ invalidate();
+ }
+
+ public void setSplitBackground(Drawable bg) {
+ mSplitBackground = bg;
+ invalidate();
+ }
+
/**
* Set the action bar into a "transitioning" state. While transitioning
* the bar will block focus and touch from all of its descendants. This
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 5baed75..b7bc366 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -444,7 +444,9 @@
mTextView.setText(null);
}
- setContentDescription(tab.getContentDescription());
+ if (mIconView != null) {
+ mIconView.setContentDescription(tab.getContentDescription());
+ }
}
}
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index b01dcd8..9313d0a 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -760,7 +760,8 @@
jint count = end - start;
sp<TextLayoutCacheValue> value;
#if USE_TEXT_LAYOUT_CACHE
- value = gTextLayoutCache.getValue(paint, textArray, start, count, end, flags);
+ value = TextLayoutCache::getInstance().getValue(paint, textArray, start, count,
+ end, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
@@ -780,7 +781,8 @@
sp<TextLayoutCacheValue> value;
#if USE_TEXT_LAYOUT_CACHE
- value = gTextLayoutCache.getValue(paint, textArray, start, count, contextCount, flags);
+ value = TextLayoutCache::getInstance().getValue(paint, textArray, start, count,
+ contextCount, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 7e89a37..fa9a7b7 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -257,7 +257,7 @@
sp<TextLayoutCacheValue> value;
#if USE_TEXT_LAYOUT_CACHE
// Return advances from the cache. Compute them if needed
- value = gTextLayoutCache.getValue(
+ value = TextLayoutCache::getInstance().getValue(
paint, chars, start, count, contextCount, dirFlags);
#else
value = new TextLayoutCacheValue();
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index 9bb1b92..0a29d78 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -41,11 +41,6 @@
*/
#define USE_TEXT_LAYOUT_CACHE 1
-
-#if USE_TEXT_LAYOUT_CACHE
- static TextLayoutCache gTextLayoutCache;
-#endif
-
enum {
kBidi_LTR = 0,
kBidi_RTL = 1,
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index f04c5eb5..7f79277 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "TextLayoutCache"
+
#include "TextLayoutCache.h"
#include "TextLayout.h"
@@ -23,6 +25,12 @@
namespace android {
+//--------------------------------------------------------------------------------------------------
+#if USE_TEXT_LAYOUT_CACHE
+ ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);
+#endif
+//--------------------------------------------------------------------------------------------------
+
TextLayoutCache::TextLayoutCache() :
mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity),
mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)),
@@ -30,13 +38,6 @@
init();
}
-TextLayoutCache::TextLayoutCache(uint32_t max):
- mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity),
- mSize(0), mMaxSize(max),
- mCacheHitCount(0), mNanosecondsSaved(0) {
- init();
-}
-
TextLayoutCache::~TextLayoutCache() {
mCache.clear();
}
@@ -46,25 +47,21 @@
mDebugLevel = readRtlDebugLevel();
mDebugEnabled = mDebugLevel & kRtlDebugCaches;
- LOGD("Using TextLayoutCache debug level: %d - Debug Enabled: %d", mDebugLevel, mDebugEnabled);
+ LOGD("Using debug level: %d - Debug Enabled: %d", mDebugLevel, mDebugEnabled);
mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
- if (mDebugEnabled) {
- LOGD("TextLayoutCache start time: %lld", mCacheStartTime);
- }
- mInitialized = true;
if (mDebugEnabled) {
+ LOGD("Start time: %lld", mCacheStartTime);
#if RTL_USE_HARFBUZZ
- LOGD("TextLayoutCache is using HARFBUZZ");
+ LOGD("Using HARFBUZZ");
#else
- LOGD("TextLayoutCache is using ICU");
+ LOGD("Using ICU");
#endif
+ LOGD("Initialization is done");
}
- if (mDebugEnabled) {
- LOGD("TextLayoutCache initialization is done");
- }
+ mInitialized = true;
}
/*
@@ -147,8 +144,7 @@
// Cleanup to make some room if needed
if (mSize + size > mMaxSize) {
if (mDebugEnabled) {
- LOGD("TextLayoutCache: need to clean some entries "
- "for making some room for a new entry");
+ LOGD("Need to clean some entries for making some room for a new entry");
}
while (mSize + size > mMaxSize) {
// This will call the callback
@@ -213,7 +209,7 @@
float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
LOGD("------------------------------------------------");
- LOGD("TextLayoutCache stats");
+ LOGD("Cache stats");
LOGD("------------------------------------------------");
LOGD("pid : %d", getpid());
LOGD("running : %.0f seconds", timeRunningInSec);
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 10dee87..0d8d71f 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -25,6 +25,7 @@
#include <utils/GenerationCache.h>
#include <utils/Compare.h>
#include <utils/RefBase.h>
+#include <utils/Singleton.h>
#include <SkPaint.h>
#include <SkTemplates.h>
@@ -187,11 +188,11 @@
/**
* Cache of text layout information.
*/
-class TextLayoutCache : public OnEntryRemoved<TextLayoutCacheKey, sp<TextLayoutCacheValue> >
+class TextLayoutCache : public OnEntryRemoved<TextLayoutCacheKey, sp<TextLayoutCacheValue> >,
+ public Singleton<TextLayoutCache>
{
public:
TextLayoutCache();
- TextLayoutCache(uint32_t maxByteSize);
virtual ~TextLayoutCache();
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 819449a..292047b 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -1659,6 +1659,7 @@
if (fileDesc == NULL) {
// FileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath);
+ close(fd);
return NULL;
}
@@ -1667,7 +1668,7 @@
if (parcelFileDesc == NULL) {
// ParcelFileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath);
- LOGE("---Parcel File Desc is null");
+ close(fd);
return NULL;
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index bcf8e71..395e417 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -477,7 +477,7 @@
#if RTL_USE_HARFBUZZ
sp<TextLayoutCacheValue> value;
#if USE_TEXT_LAYOUT_CACHE
- value = gTextLayoutCache.getValue(paint, text, 0, count, count, flags);
+ value = TextLayoutCache::getInstance().getValue(paint, text, 0, count, count, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
@@ -507,7 +507,7 @@
#if RTL_USE_HARFBUZZ
sp<TextLayoutCacheValue> value;
#if USE_TEXT_LAYOUT_CACHE
- value = gTextLayoutCache.getValue(paint, text, start, count, contextCount, flags);
+ value = TextLayoutCache::getInstance().getValue(paint, text, start, count, contextCount, flags);
if (value == NULL) {
LOGE("Cannot get TextLayoutCache value");
return ;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3fa4d48..72863a2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -87,9 +87,7 @@
<protected-broadcast android:name="android.bluetooth.device.action.NAME_FAILED" />
<protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
<protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
- <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
<protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REPLY" />
- <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
<protected-broadcast
android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
@@ -1501,19 +1499,23 @@
android:theme="@style/Theme.Holo.Dialog"
android:label="@string/heavy_weight_switcher_title"
android:finishOnCloseSystemDialogs="true"
- android:excludeFromRecents="true">
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.PlatLogoActivity"
- android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen">
+ android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.DisableCarModeActivity"
android:theme="@style/Theme.NoDisplay"
- android:excludeFromRecents="true">
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.RingtonePickerActivity"
android:theme="@style/Theme.Holo.Dialog.Alert"
android:excludeFromRecents="true"
- android:multiprocess="true">
+ android:multiprocess="true"
+ android:process=":ui">
<intent-filter>
<action android:name="android.intent.action.RINGTONE_PICKER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -1524,18 +1526,21 @@
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.Holo.Dialog"
- android:label="@string/choose_account_label">
+ android:label="@string/choose_account_label"
+ android:process=":ui">
</activity>
<activity android:name="android.accounts.GrantCredentialsPermissionActivity"
android:excludeFromRecents="true"
android:exported="true"
- android:theme="@android:style/Theme.Holo.DialogWhenLarge">
+ android:theme="@android:style/Theme.Holo.DialogWhenLarge"
+ android:process=":ui">
</activity>
<activity android:name="android.content.SyncActivityTooManyDeletes"
android:theme="@android:style/Theme.Holo.Dialog"
- android:label="@string/sync_too_many_deletes">
+ android:label="@string/sync_too_many_deletes"
+ android:process=":ui">
</activity>
<activity android:name="com.android.server.ShutdownActivity"
@@ -1553,7 +1558,8 @@
<activity android:name="com.android.internal.app.NetInitiatedActivity"
android:theme="@style/Theme.Holo.Dialog.Alert"
- android:excludeFromRecents="true">
+ android:excludeFromRecents="true"
+ android:process=":ui">
</activity>
<receiver android:name="com.android.server.BootReceiver" >
diff --git a/core/res/res/drawable-hdpi/list_divider_holo_dark.9.png b/core/res/res/drawable-hdpi/list_divider_holo_dark.9.png
deleted file mode 100644
index 7264a7a..0000000
--- a/core/res/res/drawable-hdpi/list_divider_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_divider_holo_light.9.png b/core/res/res/drawable-hdpi/list_divider_holo_light.9.png
deleted file mode 100644
index 74f48f5..0000000
--- a/core/res/res/drawable-hdpi/list_divider_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_longpressed_holo.9.png b/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
index d06549c..4ea7afa 100644
--- a/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
+++ b/core/res/res/drawable-hdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
index dae40ca..e20b02d 100644
--- a/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selected_holo_light.9.png b/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
index dae40ca..e20b02d 100644
--- a/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_holo_dark.9.png b/core/res/res/drawable-mdpi/list_divider_holo_dark.9.png
deleted file mode 100644
index aa8015d..0000000
--- a/core/res/res/drawable-mdpi/list_divider_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_holo_light.9.png b/core/res/res/drawable-mdpi/list_divider_holo_light.9.png
deleted file mode 100644
index c25c256..0000000
--- a/core/res/res/drawable-mdpi/list_divider_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_longpressed_holo.9.png b/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
index 2b8a0b3..3bf8e03 100644
--- a/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
+++ b/core/res/res/drawable-mdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
index 4cbcee9..13cb131 100644
--- a/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_selected_holo_light.9.png b/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
index 4cbcee9..13cb131 100644
--- a/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index cc46f19..0000000
--- a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index cc46f19..0000000
--- a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_divider_horizontal_holo_dark.9.png b/core/res/res/drawable-xhdpi/list_divider_horizontal_holo_dark.9.png
index a8ad54d..0c901de 100644
--- a/core/res/res/drawable-xhdpi/list_divider_horizontal_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/list_divider_horizontal_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png b/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png
index e303022..eda10e6 100644
--- a/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png
+++ b/core/res/res/drawable-xhdpi/list_longpressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png b/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png
index 4375032..ee5eb6f 100644
--- a/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/list_selected_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png b/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png
index 4375032..ee5eb6f 100644
--- a/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/list_selected_holo_light.9.png
Binary files differ
diff --git a/core/res/res/values-bg-rBG/donottranslate-cldr.xml b/core/res/res/values-bg-rBG/donottranslate-cldr.xml
deleted file mode 100644
index 4c38ad2..0000000
--- a/core/res/res/values-bg-rBG/donottranslate-cldr.xml
+++ /dev/null
@@ -1,149 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="month_long_standalone_january">януари</string>
- <string name="month_long_standalone_february">февруари</string>
- <string name="month_long_standalone_march">март</string>
- <string name="month_long_standalone_april">април</string>
- <string name="month_long_standalone_may">май</string>
- <string name="month_long_standalone_june">юни</string>
- <string name="month_long_standalone_july">юли</string>
- <string name="month_long_standalone_august">август</string>
- <string name="month_long_standalone_september">септември</string>
- <string name="month_long_standalone_october">октомври</string>
- <string name="month_long_standalone_november">ноември</string>
- <string name="month_long_standalone_december">декември</string>
-
- <string name="month_long_january">януари</string>
- <string name="month_long_february">февруари</string>
- <string name="month_long_march">март</string>
- <string name="month_long_april">април</string>
- <string name="month_long_may">май</string>
- <string name="month_long_june">юни</string>
- <string name="month_long_july">юли</string>
- <string name="month_long_august">август</string>
- <string name="month_long_september">септември</string>
- <string name="month_long_october">октомври</string>
- <string name="month_long_november">ноември</string>
- <string name="month_long_december">декември</string>
-
- <string name="month_medium_january">ян.</string>
- <string name="month_medium_february">февр.</string>
- <string name="month_medium_march">март</string>
- <string name="month_medium_april">апр.</string>
- <string name="month_medium_may">май</string>
- <string name="month_medium_june">юни</string>
- <string name="month_medium_july">юли</string>
- <string name="month_medium_august">авг.</string>
- <string name="month_medium_september">септ.</string>
- <string name="month_medium_october">окт.</string>
- <string name="month_medium_november">ноем.</string>
- <string name="month_medium_december">дек.</string>
-
- <string name="month_shortest_january">я</string>
- <string name="month_shortest_february">ф</string>
- <string name="month_shortest_march">м</string>
- <string name="month_shortest_april">а</string>
- <string name="month_shortest_may">м</string>
- <string name="month_shortest_june">ю</string>
- <string name="month_shortest_july">ю</string>
- <string name="month_shortest_august">а</string>
- <string name="month_shortest_september">с</string>
- <string name="month_shortest_october">о</string>
- <string name="month_shortest_november">н</string>
- <string name="month_shortest_december">д</string>
-
- <string name="day_of_week_long_sunday">неделя</string>
- <string name="day_of_week_long_monday">понеделник</string>
- <string name="day_of_week_long_tuesday">вторник</string>
- <string name="day_of_week_long_wednesday">сряда</string>
- <string name="day_of_week_long_thursday">четвъртък</string>
- <string name="day_of_week_long_friday">петък</string>
- <string name="day_of_week_long_saturday">събота</string>
-
- <string name="day_of_week_medium_sunday">нд</string>
- <string name="day_of_week_medium_monday">пн</string>
- <string name="day_of_week_medium_tuesday">вт</string>
- <string name="day_of_week_medium_wednesday">ср</string>
- <string name="day_of_week_medium_thursday">чт</string>
- <string name="day_of_week_medium_friday">пт</string>
- <string name="day_of_week_medium_saturday">сб</string>
-
- <string name="day_of_week_short_sunday">нд</string>
- <string name="day_of_week_short_monday">пн</string>
- <string name="day_of_week_short_tuesday">вт</string>
- <string name="day_of_week_short_wednesday">ср</string>
- <string name="day_of_week_short_thursday">чт</string>
- <string name="day_of_week_short_friday">пт</string>
- <string name="day_of_week_short_saturday">сб</string>
-
- <string name="day_of_week_shortest_sunday">н</string>
- <string name="day_of_week_shortest_monday">п</string>
- <string name="day_of_week_shortest_tuesday">в</string>
- <string name="day_of_week_shortest_wednesday">с</string>
- <string name="day_of_week_shortest_thursday">ч</string>
- <string name="day_of_week_shortest_friday">п</string>
- <string name="day_of_week_shortest_saturday">с</string>
-
- <string name="am">пр. об.</string>
- <string name="pm">сл. об.</string>
- <string name="yesterday">Вчера</string>
- <string name="today">Днес</string>
- <string name="tomorrow">Утре</string>
-
- <string name="hour_minute_24">%-k:%M</string>
- <string name="hour_minute_ampm">%-l:%M %p</string>
- <string name="hour_minute_cap_ampm">%-l:%M %p</string>
- <string name="twelve_hour_time_format">h:mm a</string>
- <string name="twenty_four_hour_time_format">H:mm</string>
- <string name="numeric_date">%d.%m.%Y</string>
- <string name="numeric_date_format">dd.MM.yyyy</string>
- <string name="numeric_date_template">"%s.%s.%s"</string>
- <string name="month_day_year">%d %B %Y</string>
- <string name="time_of_day">%H:%M:%S</string>
- <string name="date_and_time">%H:%M:%S %d.%m.%Y</string>
- <string name="date_time">%2$s %1$s</string>
- <string name="time_date">%1$s %3$s</string>
- <string name="abbrev_month_day_year">%d.%m.%Y</string>
- <string name="month_day">%-e %B</string>
- <string name="month">%-B</string>
- <string name="month_year">%B %Y</string>
- <string name="abbrev_month_day">%-e %b</string>
- <string name="abbrev_month">%b</string>
- <string name="abbrev_month_year">%b %Y</string>
- <string name="time1_time2">%1$s-%2$s</string>
- <string name="date1_date2">%2$s - %5$s</string>
- <string name="numeric_md1_md2">%3$s.%2$s - %8$s.%7$s</string>
- <string name="numeric_wday1_md1_wday2_md2">%3$s.%2$s, %1$s - %8$s.%7$s, %6$s</string>
- <string name="numeric_mdy1_mdy2">%3$s.%2$s.%4$s - %8$s.%7$s.%9$s</string>
- <string name="numeric_wday1_mdy1_wday2_mdy2">%3$s.%2$s.%4$s, %1$s - %8$s.%7$s.%9$s, %6$s</string>
- <string name="numeric_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s.%2$s.%4$s, %1$s - %10$s %8$s.%7$s.%9$s, %6$s</string>
- <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string>
- <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %3$s.%2$s, %1$s - %10$s %8$s.%7$s, %6$s</string>
- <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string>
- <string name="wday1_date1_time1_wday2_date2_time2">%3$s %2$s, %1$s - %6$s %5$s, %4$s</string>
- <string name="wday1_date1_wday2_date2">%2$s, %1$s - %5$s, %4$s</string>
- <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
- <string name="time_wday_date">%1$s %3$s, %2$s</string>
- <string name="wday_date">%3$s, %2$s</string>
- <string name="time_wday">%1$s %2$s</string>
- <string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string>
- <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s, %1$s - %8$s %7$s, %6$s</string>
- <string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
- <string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
- <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s</string>
- <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s</string>
- <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
- <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
- <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s</string>
- <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s</string>
- <string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s, %1$s - %8$s %7$s %9$s, %6$s</string>
- <string name="same_month_md1_md2">%3$s-%8$s %2$s</string>
- <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s, %1$s - %8$s %7$s, %6$s</string>
- <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
- <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
- <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s</string>
- <string name="short_format_month">%b</string>
- <string name="full_wday_month_day_no_year">EEEE MMMM d</string>
- <string name="abbrev_wday_month_day_year">d MMM yyyy, E</string>
-</resources>
diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml
index 62f550a..dc8b3ad 100644
--- a/core/res/res/values-bg/donottranslate-cldr.xml
+++ b/core/res/res/values-bg/donottranslate-cldr.xml
@@ -128,22 +128,22 @@
<string name="wday_date">%3$s, %2$s</string>
<string name="time_wday">%1$s %2$s</string>
<string name="same_year_md1_md2">%3$s %2$s - %8$s %7$s</string>
- <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string>
+ <string name="same_year_wday1_md1_wday2_md2">%3$s %2$s, %1$s - %8$s %7$s, %6$s</string>
<string name="same_year_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
<string name="same_month_md1_time1_md2_time2">%5$s %3$s %2$s - %10$s %8$s %7$s</string>
- <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string>
- <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string>
+ <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s</string>
+ <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %3$s %2$s, %1$s - %10$s %8$s %7$s, %6$s</string>
<string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
<string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s %2$s %4$s - %10$s %8$s %7$s %9$s</string>
<string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s</string>
<string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %3$s %2$s %4$s, %1$s - %10$s %8$s %7$s %9$s, %6$s</string>
<string name="same_month_wday1_mdy1_wday2_mdy2">%3$s %2$s %4$s, %1$s - %8$s %7$s %9$s, %6$s</string>
<string name="same_month_md1_md2">%3$s-%8$s %2$s</string>
- <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string>
+ <string name="same_month_wday1_md1_wday2_md2">%3$s %2$s, %1$s - %8$s %7$s, %6$s</string>
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s</string>
<string name="short_format_month">%b</string>
- <string name="full_wday_month_day_no_year">E, d MMMM</string>
- <string name="abbrev_wday_month_day_year">d MMM y, E</string>
+ <string name="full_wday_month_day_no_year">d MMMM, EEEE</string>
+ <string name="abbrev_wday_month_day_year">d MMM yyyy, E</string>
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index f434ce8..6f98e02 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -878,8 +878,8 @@
<item name="textColorSearchUrl">@android:color/search_url_text_holo</item>
<item name="textColorHighlight">@android:color/highlighted_text_holo_dark</item>
<item name="textColorHighlightInverse">@android:color/highlighted_text_holo_light</item>
- <item name="textColorLink">@android:color/link_text_holo_dark</item>
- <item name="textColorLinkInverse">@android:color/link_text_holo_light</item>
+ <item name="textColorLink">@android:color/holo_blue_light</item>
+ <item name="textColorLinkInverse">@android:color/holo_blue_light</item>
<item name="textColorAlertDialogListItem">@android:color/primary_text_holo_dark</item>
<item name="textAppearanceLarge">@android:style/TextAppearance.Holo.Large</item>
@@ -1182,8 +1182,8 @@
<item name="textColorSearchUrl">@android:color/search_url_text_holo</item>
<item name="textColorHighlight">@android:color/highlighted_text_holo_light</item>
<item name="textColorHighlightInverse">@android:color/highlighted_text_holo_dark</item>
- <item name="textColorLink">@android:color/link_text_holo_light</item>
- <item name="textColorLinkInverse">@android:color/link_text_holo_dark</item>
+ <item name="textColorLink">@android:color/holo_blue_light</item>
+ <item name="textColorLinkInverse">@android:color/holo_blue_light</item>
<item name="textColorAlertDialogListItem">@android:color/primary_text_holo_light</item>
<item name="textAppearanceLarge">@android:style/TextAppearance.Holo.Light.Large</item>
diff --git a/data/etc/android.hardware.wifi.direct.xml b/data/etc/android.hardware.wifi.direct.xml
new file mode 100644
index 0000000..78cb474
--- /dev/null
+++ b/data/etc/android.hardware.wifi.direct.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<!-- This is the standard feature indicating that the device includes WiFi Direct. -->
+<permissions>
+ <feature name="android.hardware.wifi.direct" />
+</permissions>
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
index ba9d76f..ff97670 100644
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 72d233a3..0a3deb1 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -290,6 +290,8 @@
/**
* Implement this interface if you want to create an drawable that is RTL aware
+ *
+ * @hide
*/
public static interface Callback2 extends Callback {
/**
@@ -379,6 +381,8 @@
/**
* Use the current {@link android.graphics.drawable.Drawable.Callback2} implementation to get
* the resolved layout direction of this Drawable.
+ *
+ * @hide
*/
public int getResolvedLayoutDirectionSelf() {
final Callback callback = getCallback();
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 392193b..5ccf87f 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -78,12 +78,8 @@
// conditionals don't get stripped... but that is probably what we want.
#if !LOG_NDEBUG
static const char *kReturnStrings[] = {
-#if 1 /* TODO: error update strings */
- "unknown",
-#else
+ "BR_ERROR",
"BR_OK",
- "BR_TIMEOUT",
- "BR_WAKEUP",
"BR_TRANSACTION",
"BR_REPLY",
"BR_ACQUIRE_RESULT",
@@ -94,25 +90,19 @@
"BR_RELEASE",
"BR_DECREFS",
"BR_ATTEMPT_ACQUIRE",
- "BR_EVENT_OCCURRED",
"BR_NOOP",
"BR_SPAWN_LOOPER",
"BR_FINISHED",
"BR_DEAD_BINDER",
- "BR_CLEAR_DEATH_NOTIFICATION_DONE"
-#endif
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE",
+ "BR_FAILED_REPLY"
};
static const char *kCommandStrings[] = {
-#if 1 /* TODO: error update strings */
- "unknown",
-#else
- "BC_NOOP",
"BC_TRANSACTION",
"BC_REPLY",
"BC_ACQUIRE_RESULT",
"BC_FREE_BUFFER",
- "BC_TRANSACTION_COMPLETE",
"BC_INCREFS",
"BC_ACQUIRE",
"BC_RELEASE",
@@ -120,18 +110,12 @@
"BC_INCREFS_DONE",
"BC_ACQUIRE_DONE",
"BC_ATTEMPT_ACQUIRE",
- "BC_RETRIEVE_ROOT_OBJECT",
- "BC_SET_THREAD_ENTRY",
"BC_REGISTER_LOOPER",
"BC_ENTER_LOOPER",
"BC_EXIT_LOOPER",
- "BC_SYNC",
- "BC_STOP_PROCESS",
- "BC_STOP_SELF",
"BC_REQUEST_DEATH_NOTIFICATION",
"BC_CLEAR_DEATH_NOTIFICATION",
"BC_DEAD_BINDER_DONE"
-#endif
};
static const char* getReturnString(size_t idx)
@@ -154,30 +138,36 @@
{
const binder_transaction_data* btd =
(const binder_transaction_data*)data;
- out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl
+ if (btd->target.handle < 1024) {
+ /* want to print descriptors in decimal; guess based on value */
+ out << "target.desc=" << btd->target.handle;
+ } else {
+ out << "target.ptr=" << btd->target.ptr;
+ }
+ out << " (cookie " << btd->cookie << ")" << endl
<< "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl
<< "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
<< " bytes)" << endl
<< "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
- << " bytes)" << endl;
+ << " bytes)";
return btd+1;
}
static const void* printReturnCommand(TextOutput& out, const void* _cmd)
{
- static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
-
+ static const size_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
const int32_t* cmd = (const int32_t*)_cmd;
int32_t code = *cmd++;
- if (code == BR_ERROR) {
+ size_t cmdIndex = code & 0xff;
+ if (code == (int32_t) BR_ERROR) {
out << "BR_ERROR: " << (void*)(*cmd++) << endl;
return cmd;
- } else if (code < 0 || code >= N) {
+ } else if (cmdIndex >= N) {
out << "Unknown reply: " << code << endl;
return cmd;
}
+ out << kReturnStrings[cmdIndex];
- out << kReturnStrings[code];
switch (code) {
case BR_TRANSACTION:
case BR_REPLY: {
@@ -213,6 +203,11 @@
const int32_t c = *cmd++;
out << ": death cookie " << (void*)c;
} break;
+
+ default:
+ // no details to show for: BR_OK, BR_DEAD_REPLY,
+ // BR_TRANSACTION_COMPLETE, BR_FINISHED
+ break;
}
out << endl;
@@ -221,16 +216,17 @@
static const void* printCommand(TextOutput& out, const void* _cmd)
{
- static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
-
+ static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
const int32_t* cmd = (const int32_t*)_cmd;
int32_t code = *cmd++;
- if (code < 0 || code >= N) {
+ size_t cmdIndex = code & 0xff;
+
+ if (cmdIndex >= N) {
out << "Unknown command: " << code << endl;
return cmd;
}
-
- out << kCommandStrings[code];
+ out << kCommandStrings[cmdIndex];
+
switch (code) {
case BC_TRANSACTION:
case BC_REPLY: {
@@ -254,7 +250,7 @@
case BC_RELEASE:
case BC_DECREFS: {
const int32_t d = *cmd++;
- out << ": descriptor=" << (void*)d;
+ out << ": desc=" << d;
} break;
case BC_INCREFS_DONE:
@@ -267,7 +263,7 @@
case BC_ATTEMPT_ACQUIRE: {
const int32_t p = *cmd++;
const int32_t d = *cmd++;
- out << ": decriptor=" << (void*)d << ", pri=" << p;
+ out << ": desc=" << d << ", pri=" << p;
} break;
case BC_REQUEST_DEATH_NOTIFICATION:
@@ -281,6 +277,11 @@
const int32_t c = *cmd++;
out << ": death cookie " << (void*)c;
} break;
+
+ default:
+ // no details to show for: BC_REGISTER_LOOPER, BC_ENTER_LOOPER,
+ // BC_EXIT_LOOPER
+ break;
}
out << endl;
@@ -592,6 +593,7 @@
status_t IPCThreadState::attemptIncStrongHandle(int32_t handle)
{
+ LOG_REMOTEREFS("IPCThreadState::attemptIncStrongHandle(%d)\n", handle);
mOut.writeInt32(BC_ATTEMPT_ACQUIRE);
mOut.writeInt32(0); // xxx was thread priority
mOut.writeInt32(handle);
@@ -772,7 +774,7 @@
} else {
bwr.read_size = 0;
}
-
+
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
if (outAvail != 0) {
@@ -789,7 +791,7 @@
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
-
+
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
@@ -809,7 +811,7 @@
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
} while (err == -EINTR);
-
+
IF_LOG_COMMANDS() {
alog << "Our err: " << (void*)err << ", write consumed: "
<< bwr.write_consumed << " (of " << mOut.dataSize()
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 7e8c7fd..349b9e3 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -91,6 +91,13 @@
#endif
}
+// TODO: This implementation is flawed and can generate T-junctions
+// in the mesh, which will in turn produce cracks when the
+// mesh is rotated/skewed. The easiest way to fix this would
+// be, for each row, to add new vertices shared with the previous
+// row when the two rows share an edge.
+// In practice, T-junctions do not appear often so this has yet
+// to be fixed.
void LayerRenderer::generateMesh() {
#if RENDER_LAYERS_AS_REGIONS
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a20a88e..32595e4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -63,36 +63,42 @@
// In this array, the index of each Blender equals the value of the first
// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
static const Blender gBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
+ { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
+ { SkXfermode::kMultiply_Mode, GL_ZERO, GL_SRC_COLOR },
+ { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
};
// This array contains the swapped version of each SkXfermode. For instance
// this array's SrcOver blending mode is actually DstOver. You can refer to
// createLayer() for more information on the purpose of this array.
static const Blender gBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
+ { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
+ { SkXfermode::kMultiply_Mode, GL_DST_COLOR, GL_ZERO },
+ { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
};
static const GLenum gTextureUnits[] = {
@@ -724,6 +730,8 @@
return;
}
+ // TODO: See LayerRenderer.cpp::generateMesh() for important
+ // information about this implementation
if (!layer->region.isEmpty()) {
size_t count;
const android::Rect* rects = layer->region.getArray(&count);
@@ -2487,7 +2495,7 @@
ProgramDescription& description, bool swapSrcDst) {
blend = blend || mode != SkXfermode::kSrcOver_Mode;
if (blend) {
- if (mode < SkXfermode::kPlus_Mode) {
+ if (mode <= SkXfermode::kScreen_Mode) {
if (!mCaches.blend) {
glEnable(GL_BLEND);
}
@@ -2540,15 +2548,7 @@
void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
if (paint) {
- if (!mCaches.extensions.hasFramebufferFetch()) {
- const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
- if (!isMode) {
- // Assume SRC_OVER
- *mode = SkXfermode::kSrcOver_Mode;
- }
- } else {
- *mode = getXfermode(paint->getXfermode());
- }
+ *mode = getXfermode(paint->getXfermode());
// Skia draws using the color's alpha channel if < 255
// Otherwise, it uses the paint's alpha
diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp
index 4dfa578..ceca435 100644
--- a/libs/utils/Static.cpp
+++ b/libs/utils/Static.cpp
@@ -56,7 +56,9 @@
protected:
virtual status_t writeLines(const struct iovec& vec, size_t N)
{
- android_writevLog(&vec, N);
+ //android_writevLog(&vec, N); <-- this is now a no-op
+ if (N != 1) LOGI("WARNING: writeLines N=%d\n", N);
+ LOGI("%.*s", vec.iov_len, (const char*) vec.iov_base);
return NO_ERROR;
}
};
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index aa0a2e9..d7b8eaa 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -3758,65 +3758,33 @@
/**
* This method extracts a frame from the input file
- * and returns the frame as a bitmap
- *
- * @param inputFile The inputFile
- * @param width The width of the output frame
- * @param height The height of the output frame
- * @param timeMS The time in ms at which the frame has to be extracted
+ * and returns the frame as a bitmap. See getPixelsList() for more information.
*/
- Bitmap getPixels(String inputFile, int width, int height, long timeMS) {
- if (inputFile == null) {
- throw new IllegalArgumentException("Invalid input file");
- }
-
- /* Make width and height as even */
- final int newWidth = (width + 1) & 0xFFFFFFFE;
- final int newHeight = (height + 1) & 0xFFFFFFFE;
-
- /* Create a temp bitmap for resized thumbnails */
- Bitmap tempBitmap = null;
- if ((newWidth != width) || (newHeight != height)) {
- tempBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888);
- }
-
- IntBuffer rgb888 = IntBuffer.allocate(newWidth * newHeight * 4);
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- nativeGetPixels(inputFile, rgb888.array(), newWidth, newHeight, timeMS);
-
- if ((newWidth == width) && (newHeight == height)) {
- bitmap.copyPixelsFromBuffer(rgb888);
- } else {
- /* Create a temp bitmap to be used for resize */
- tempBitmap.copyPixelsFromBuffer(rgb888);
-
- /* Create a canvas to resize */
- final Canvas canvas = new Canvas(bitmap);
- canvas.drawBitmap(tempBitmap, new Rect(0, 0, newWidth, newHeight),
- new Rect(0, 0, width, height), sResizePaint);
- canvas.setBitmap(null);
- }
-
- if (tempBitmap != null) {
- tempBitmap.recycle();
- }
-
- return bitmap;
+ Bitmap getPixels(String filename, int width, int height, long timeMs,
+ int videoRotation) {
+ final Bitmap result[] = new Bitmap[1];
+ getPixelsList(filename, width, height, timeMs, timeMs, 1, new int[] {0},
+ new MediaItem.GetThumbnailListCallback() {
+ public void onThumbnail(Bitmap bitmap, int index) {
+ result[0] = bitmap;
+ }
+ }, videoRotation);
+ return result[0];
}
/**
* This method extracts a list of frame from the
* input file and returns the frame in bitmap array
*
- * @param filename The inputFile
- * @param width The width of the output frame
- * @param height The height of the output frame
+ * @param filename The input file name
+ * @param width The width of the output frame, before rotation
+ * @param height The height of the output frame, before rotation
* @param startMs The starting time in ms
* @param endMs The end time in ms
* @param thumbnailCount The number of frames to be extracted
* @param indices The indices of thumbnails wanted
* @param callback The callback used to pass back the bitmaps
- * from startMs to endMs
+ * @param videoRotation The rotation degree need to be done for the bitmap
*
* @return The frames as bitmaps in bitmap array
**/
@@ -3824,62 +3792,69 @@
long startMs, long endMs, int thumbnailCount, int[] indices,
final MediaItem.GetThumbnailListCallback callback,
final int videoRotation) {
- /* Make width and height as even */
- final int newWidth = (width + 1) & 0xFFFFFFFE;
- final int newHeight = (height + 1) & 0xFFFFFFFE;
- final int thumbnailSize = newWidth * newHeight;
- /* Create a temp bitmap for resized thumbnails */
- final Bitmap tempBitmap =
- (newWidth != width || newHeight != height)
- ? Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888)
+ // The decoder needs output width and height as even
+ final int decWidth = (width + 1) & 0xFFFFFFFE;
+ final int decHeight = (height + 1) & 0xFFFFFFFE;
+ final int thumbnailSize = decWidth * decHeight;
+
+ // We convert the decoder output (in int[]) to a bitmap by first
+ // copy it into an IntBuffer, then use Bitmap.copyPixelsFromBuffer to
+ // copy it to the bitmap.
+ final int[] decArray = new int[thumbnailSize];
+ final IntBuffer decBuffer = IntBuffer.allocate(thumbnailSize);
+
+ // If we need to resize and/or rotate the decoder output, we need a
+ // temporary bitmap to hold the decoded output.
+ final boolean needToMassage =
+ (decWidth != width || decHeight != height || videoRotation != 0);
+ final Bitmap tmpBitmap = needToMassage
+ ? Bitmap.createBitmap(decWidth, decHeight, Bitmap.Config.ARGB_8888)
: null;
- final int[] rgb888 = new int[thumbnailSize];
- final IntBuffer tmpBuffer = IntBuffer.allocate(thumbnailSize);
- nativeGetPixelsList(filename, rgb888, newWidth, newHeight,
- thumbnailCount, videoRotation, startMs, endMs, indices,
+ // The final output bitmap width/height may swap because of rotation.
+ final boolean needToSwapWH = (videoRotation == 90 || videoRotation == 270);
+ final int outWidth = needToSwapWH ? height : width;
+ final int outHeight = needToSwapWH ? width : height;
+
+ nativeGetPixelsList(filename, decArray, decWidth, decHeight,
+ thumbnailCount, startMs, endMs, indices,
new NativeGetPixelsListCallback() {
public void onThumbnail(int index) {
- Bitmap bitmap = Bitmap.createBitmap(
- width, height, Bitmap.Config.ARGB_8888);
- tmpBuffer.put(rgb888, 0, thumbnailSize);
- tmpBuffer.rewind();
+ // This is the bitmap we will output to the client
+ Bitmap outBitmap = Bitmap.createBitmap(
+ outWidth, outHeight, Bitmap.Config.ARGB_8888);
- if ((newWidth == width) && (newHeight == height)) {
- bitmap.copyPixelsFromBuffer(tmpBuffer);
+ // Copy int[] to IntBuffer
+ decBuffer.put(decArray, 0, thumbnailSize);
+ decBuffer.rewind();
+
+ if (!needToMassage) {
+ // We can directly read the decoded result to output bitmap
+ outBitmap.copyPixelsFromBuffer(decBuffer);
} else {
- /* Copy the out rgb buffer to temp bitmap */
- tempBitmap.copyPixelsFromBuffer(tmpBuffer);
+ // Copy the decoded result to an intermediate bitmap first
+ tmpBitmap.copyPixelsFromBuffer(decBuffer);
- /* Create a canvas to resize */
- final Canvas canvas = new Canvas(bitmap);
- canvas.drawBitmap(tempBitmap,
- new Rect(0, 0, newWidth, newHeight),
- new Rect(0, 0, width, height), sResizePaint);
-
- canvas.setBitmap(null);
+ // Create a canvas to resize/rotate the bitmap
+ // First scale the decoded bitmap to (0,0)-(1,1), rotate it
+ // with (0.5, 0.5) as center, then scale it to
+ // (outWidth, outHeight).
+ final Canvas canvas = new Canvas(outBitmap);
+ Matrix m = new Matrix();
+ float sx = 1f / decWidth;
+ float sy = 1f / decHeight;
+ m.postScale(sx, sy);
+ m.postRotate(videoRotation, 0.5f, 0.5f);
+ m.postScale(outWidth, outHeight);
+ canvas.drawBitmap(tmpBitmap, m, sResizePaint);
}
-
- if (videoRotation == 0) {
- callback.onThumbnail(bitmap, index);
- } else {
- Matrix mtx = new Matrix();
- mtx.postRotate(videoRotation);
- Bitmap rotatedBmp =
- Bitmap.createBitmap(bitmap, 0, 0, width, height, mtx, false);
- callback.onThumbnail(rotatedBmp, index);
-
- if (bitmap != null) {
- bitmap.recycle();
- }
- }
-
+ callback.onThumbnail(outBitmap, index);
}
});
- if (tempBitmap != null) {
- tempBitmap.recycle();
+ if (tmpBitmap != null) {
+ tmpBitmap.recycle();
}
}
@@ -3996,7 +3971,7 @@
long timeMS);
private native int nativeGetPixelsList(String fileName, int[] pixelArray,
- int width, int height, int nosofTN, int videoRotation, long startTimeMs,
+ int width, int height, int nosofTN, long startTimeMs,
long endTimeMs, int[] indices, NativeGetPixelsListCallback callback);
/**
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index 65a9e19..a862d00 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -606,7 +606,7 @@
public Bitmap getThumbnail(int width, int height, long timeMs) throws IOException {
if (getGeneratedImageClip() != null) {
return mMANativeHelper.getPixels(getGeneratedImageClip(),
- width, height,timeMs);
+ width, height, timeMs, 0);
} else {
return scaleImage(mFilename, width, height);
}
diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index 2ce857c..bbcdf57 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -293,7 +293,14 @@
throw new IllegalArgumentException("Invalid Dimensions");
}
- return mMANativeHelper.getPixels(super.getFilename(), width, height,timeMs);
+ if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
+ int temp = width;
+ width = height;
+ height = temp;
+ }
+
+ return mMANativeHelper.getPixels(
+ getFilename(), width, height, timeMs, mVideoRotationDegree);
}
/*
@@ -318,8 +325,14 @@
throw new IllegalArgumentException("Invalid dimension");
}
- mMANativeHelper.getPixelsList(super.getFilename(), width,
- height, startMs, endMs, thumbnailCount, indices, callback,
+ if (mVideoRotationDegree == 90 || mVideoRotationDegree == 270) {
+ int temp = width;
+ width = height;
+ height = temp;
+ }
+
+ mMANativeHelper.getPixelsList(getFilename(), width, height,
+ startMs, endMs, thumbnailCount, indices, callback,
mVideoRotationDegree);
}
diff --git a/media/java/android/media/videoeditor/VideoEditorImpl.java b/media/java/android/media/videoeditor/VideoEditorImpl.java
index f18dd88..2446c2f 100755
--- a/media/java/android/media/videoeditor/VideoEditorImpl.java
+++ b/media/java/android/media/videoeditor/VideoEditorImpl.java
@@ -1825,27 +1825,10 @@
if (mMediaItems.size() > 0) {
MediaItem mI = mMediaItems.get(0);
/*
- * Lets initialize the width for default aspect ratio i.e 16:9
+ * Keep aspect ratio of the image
*/
int height = 480;
- int width = 854;
- switch (mI.getAspectRatio()) {
- case MediaProperties.ASPECT_RATIO_3_2:
- width = 720;
- break;
- case MediaProperties.ASPECT_RATIO_4_3:
- width = 640;
- break;
- case MediaProperties.ASPECT_RATIO_5_3:
- width = 800;
- break;
- case MediaProperties.ASPECT_RATIO_11_9:
- width = 586;
- break;
- case MediaProperties.ASPECT_RATIO_16_9:
- case MediaProperties.ASPECT_RATIO_UNDEFINED:
- break;
- }
+ int width = mI.getWidth() * height / mI.getHeight();
Bitmap projectBitmap = null;
String filename = mI.getFilename();
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index 4e73581..4e954fc 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -185,7 +185,6 @@
M4OSA_UInt32 width,
M4OSA_UInt32 height,
M4OSA_UInt32 noOfThumbnails,
- M4OSA_UInt32 videoRotation,
jlong startTime,
jlong endTime,
jintArray indexArray,
@@ -292,7 +291,7 @@
(void *)videoEditor_release },
{"nativeGetPixels", "(Ljava/lang/String;[IIIJ)I",
(void*)videoEditor_getPixels },
- {"nativeGetPixelsList", "(Ljava/lang/String;[IIIIIJJ[ILandroid/media/videoeditor/MediaArtistNativeHelper$NativeGetPixelsListCallback;)I",
+ {"nativeGetPixelsList", "(Ljava/lang/String;[IIIIJJ[ILandroid/media/videoeditor/MediaArtistNativeHelper$NativeGetPixelsListCallback;)I",
(void*)videoEditor_getPixelsList },
{"getMediaProperties",
"(Ljava/lang/String;)Landroid/media/videoeditor/MediaArtistNativeHelper$Properties;",
@@ -2286,7 +2285,6 @@
M4OSA_UInt32 width,
M4OSA_UInt32 height,
M4OSA_UInt32 noOfThumbnails,
- M4OSA_UInt32 videoRotation,
jlong startTime,
jlong endTime,
jintArray indexArray,
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 7509239..c2c6715 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1078,6 +1078,11 @@
frames = mRemainingFrames;
}
+ int32_t waitCount = -1;
+ if (mUpdatePeriod || (!mMarkerReached && mMarkerPosition) || mLoopCount) {
+ waitCount = 1;
+ }
+
do {
audioBuffer.frameCount = frames;
@@ -1085,7 +1090,7 @@
// Calling obtainBuffer() with a wait count of 1
// limits wait time to WAIT_PERIOD_MS. This prevents from being
// stuck here not being able to handle timed events (position, markers, loops).
- status_t err = obtainBuffer(&audioBuffer, 1);
+ status_t err = obtainBuffer(&audioBuffer, waitCount);
if (err < NO_ERROR) {
if (err != TIMED_OUT) {
LOGE_IF(err != status_t(NO_MORE_BUFFERS), "Error obtaining an audio buffer, giving up.");
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index bd89ad8..50a41ca 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -108,6 +108,7 @@
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeStrongBinder(source->asBinder());
+ remote()->transact(SET_DATA_SOURCE_STREAM, data, &reply);
return reply.readInt32();
}
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index d41ab1b..ba076f5 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -20,8 +20,8 @@
#include <binder/IPCThreadState.h>
#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/AudioPlayer.h>
-#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -60,7 +60,7 @@
}
void AudioPlayer::setSource(const sp<MediaSource> &source) {
- CHECK_EQ(mSource, NULL);
+ CHECK(mSource == NULL);
mSource = source;
}
@@ -466,6 +466,8 @@
}
int64_t AudioPlayer::getRealTimeUsLocked() const {
+ CHECK(mStarted);
+ CHECK_NE(mSampleRate, 0);
return -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 47224cc..07a46bd 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1736,7 +1736,9 @@
modifyFlags(TEXT_RUNNING, SET);
}
- TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
+ TimeSource *ts =
+ ((mFlags & AUDIO_AT_EOS) || !(mFlags & AUDIOPLAYER_STARTED))
+ ? &mSystemTimeSource : mTimeSource;
if (mFlags & FIRST_FRAME) {
modifyFlags(FIRST_FRAME, CLEAR);
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 50dd804..306f1f6 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-// #define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#define LOG_TAG "SurfaceMediaSource"
#include <media/stagefright/SurfaceMediaSource.h>
@@ -458,6 +458,10 @@
LOGV("queueBuffer");
Mutex::Autolock lock(mMutex);
+ *outWidth = mDefaultWidth;
+ *outHeight = mDefaultHeight;
+ *outTransform = 0;
+
if (bufIndex < 0 || bufIndex >= mBufferCount) {
LOGE("queueBuffer: slot index out of range [0, %d]: %d",
mBufferCount, bufIndex);
@@ -518,9 +522,6 @@
// buffer is available
onFrameReceivedLocked();
- *outWidth = mDefaultWidth;
- *outHeight = mDefaultHeight;
- *outTransform = 0;
return OK;
}
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index 3ef7b71..ffa3356 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -93,7 +93,7 @@
void advance();
void reset();
- void seek(int64_t seekTimeUs);
+ void seek(int64_t seekTimeUs, bool seekToKeyFrame);
const mkvparser::Block *block() const;
int64_t blockTimeUs() const;
@@ -137,6 +137,7 @@
sp<MatroskaExtractor> mExtractor;
size_t mTrackIndex;
Type mType;
+ bool mIsAudio;
BlockIterator mBlockIter;
size_t mNALSizeLen; // for type AVC
@@ -156,6 +157,7 @@
: mExtractor(extractor),
mTrackIndex(index),
mType(OTHER),
+ mIsAudio(false),
mBlockIter(mExtractor.get(),
mExtractor->mTracks.itemAt(index).mTrackNum),
mNALSizeLen(0) {
@@ -164,6 +166,8 @@
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
+ mIsAudio = !strncasecmp("audio/", mime, 6);
+
if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
mType = AVC;
@@ -299,7 +303,7 @@
} while (!eos() && block()->GetTrackNumber() != mTrackNum);
}
-void BlockIterator::seek(int64_t seekTimeUs) {
+void BlockIterator::seek(int64_t seekTimeUs, bool seekToKeyFrame) {
Mutex::Autolock autoLock(mExtractor->mLock);
mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
@@ -311,8 +315,10 @@
}
while (!eos() && block()->GetTrackNumber() != mTrackNum);
- while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
- advance_l();
+ if (seekToKeyFrame) {
+ while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
+ advance_l();
+ }
}
}
@@ -396,7 +402,11 @@
if (options && options->getSeekTo(&seekTimeUs, &mode)
&& !mExtractor->isLiveStreaming()) {
clearPendingFrames();
- mBlockIter.seek(seekTimeUs);
+
+ // Apparently keyframe indication in audio tracks is unreliable,
+ // fortunately in all our currently supported audio encodings every
+ // frame is effectively a keyframe.
+ mBlockIter.seek(seekTimeUs, !mIsAudio);
}
again:
@@ -537,6 +547,13 @@
return;
}
+#if 0
+ const mkvparser::SegmentInfo *info = mSegment->GetInfo();
+ LOGI("muxing app: %s, writing app: %s",
+ info->GetMuxingAppAsUTF8(),
+ info->GetWritingAppAsUTF8());
+#endif
+
addTracks();
}
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index d663602..d7bb703 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -156,8 +156,6 @@
eglDestroySurface(mEglDisplay, mEglSurface);
}
if (mEglDisplay != EGL_NO_DISPLAY) {
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
- EGL_NO_CONTEXT);
eglTerminate(mEglDisplay);
}
ASSERT_EQ(EGL_SUCCESS, eglGetError());
@@ -461,6 +459,7 @@
// The following call dequeues and queues the buffer
eglSwapBuffers(mEglDisplay, mEglSurface);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
glDisable(GL_SCISSOR_TEST);
}
@@ -796,7 +795,12 @@
LOGV("framesCount = %d", nFramesCount);
}
- ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+
writer.stop();
}
// Test to examine whether we can render GL buffers in to the surface
@@ -875,7 +879,12 @@
LOGV("framesCount = %d", nFramesCount);
}
- ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+
LOGV("Stopping MediaRecorder...");
CHECK_EQ(OK, mr->stop());
mr.clear();
@@ -913,7 +922,12 @@
LOGV("framesCount = %d", nFramesCount);
}
- ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL));
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ eglDestroySurface(mEglDisplay, mEglSurface);
+ mEglSurface = EGL_NO_SURFACE;
+
LOGV("Stopping MediaRecorder...");
CHECK_EQ(OK, mr->stop());
mr.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 43905dd..10e7602 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -96,11 +96,13 @@
/* package */ final class ActivityDescription {
final ActivityManager.RecentTaskInfo recentTaskInfo;
final ResolveInfo resolveInfo;
- int taskId; // application task id for curating apps
- Intent intent; // launch intent for application
+ final int taskId; // application task id for curating apps
+ final int persistentTaskId; // persistent id
+ final Intent intent; // launch intent for application
+ final String packageName; // used to override animations (see onClick())
+ final int position; // position in list
+
Matrix matrix; // arbitrary rotation matrix to correct orientation
- String packageName; // used to override animations (see onClick())
- int position; // position in list
private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
private Drawable mIcon; // application package icon
@@ -108,11 +110,12 @@
public ActivityDescription(ActivityManager.RecentTaskInfo _recentInfo,
ResolveInfo _resolveInfo, Intent _intent,
- int _id, int _pos, String _packageName) {
+ int _pos, String _packageName) {
recentTaskInfo = _recentInfo;
resolveInfo = _resolveInfo;
intent = _intent;
- taskId = _id;
+ taskId = _recentInfo.id;
+ persistentTaskId = _recentInfo.persistentId;
position = _pos;
packageName = _packageName;
}
@@ -496,17 +499,17 @@
final String title = info.loadLabel(pm).toString();
// Drawable icon = info.loadIcon(pm);
Drawable icon = getFullResIcon(resolveInfo, pm);
- int id = recentInfo.id;
if (title != null && title.length() > 0 && icon != null) {
- if (DEBUG) Log.v(TAG, "creating activity desc for id=" + id + ", label=" + title);
+ if (DEBUG) Log.v(TAG, "creating activity desc for id="
+ + recentInfo.id + ", label=" + title);
ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(
recentInfo.persistentId);
ActivityDescription item = new ActivityDescription(recentInfo,
- resolveInfo, intent, id, index, info.packageName);
+ resolveInfo, intent, index, info.packageName);
activityDescriptions.add(item);
++index;
} else {
- if (DEBUG) Log.v(TAG, "SKIPPING item " + id);
+ if (DEBUG) Log.v(TAG, "SKIPPING item " + recentInfo.id);
}
}
}
@@ -727,7 +730,7 @@
// the task.
final ActivityManager am = (ActivityManager)
mContext.getSystemService(Context.ACTIVITY_SERVICE);
- am.removeTask(ad.taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
}
private void startApplicationDetailsActivity(String packageName) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 6ec3443..55623f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1174,7 +1174,6 @@
case MSG_OPEN_RECENTS_PANEL:
if (DEBUG) Slog.d(TAG, "opening recents panel");
if (mRecentsPanel != null) {
- disable(StatusBarManager.DISABLE_BACK);
mRecentsPanel.setVisibility(View.VISIBLE);
mRecentsPanel.show(true, true);
}
@@ -1182,7 +1181,6 @@
case MSG_CLOSE_RECENTS_PANEL:
if (DEBUG) Slog.d(TAG, "closing recents panel");
if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
- disable(StatusBarManager.DISABLE_NONE);
mRecentsPanel.show(false, true);
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index d3f9525..c19550b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -22,11 +22,9 @@
import android.content.IntentFilter;
import android.text.format.DateFormat;
import android.util.AttributeSet;
-import android.util.Slog;
-import android.widget.TextView;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
+import android.widget.TextView;
import com.android.systemui.R;
@@ -42,9 +40,10 @@
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_TIME_TICK)
- || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_TIME_TICK.equals(action)
+ || Intent.ACTION_TIME_CHANGED.equals(action)
+ || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
updateClock();
}
}
@@ -118,6 +117,7 @@
// Register for Intent broadcasts for the clock and battery
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
mContext.registerReceiver(mIntentReceiver, filter, null, null);
updateClock();
@@ -127,4 +127,3 @@
}
}
}
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index e287b7a..06798c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -20,7 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.TimeAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
@@ -37,12 +37,12 @@
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import java.util.HashSet;
+import java.util.HashMap;
public class NotificationRowLayout extends ViewGroup implements SwipeHelper.Callback {
private static final String TAG = "NotificationRowLayout";
private static final boolean DEBUG = false;
- private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
+ private static final boolean SLOW_ANIMATIONS = DEBUG;
private static final boolean ANIMATE_LAYOUT = true;
@@ -54,8 +54,8 @@
int mRowHeight = 0;
int mHeight = 0;
- HashSet<View> mAppearingViews = new HashSet<View>();
- HashSet<View> mDisappearingViews = new HashSet<View>();
+ HashMap<View, ValueAnimator> mAppearingViews = new HashMap<View, ValueAnimator>();
+ HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
private SwipeHelper mSwipeHelper;
@@ -166,8 +166,6 @@
final View childF = child;
if (ANIMATE_LAYOUT) {
- mAppearingViews.add(child);
-
child.setPivotY(0);
final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f, 1f);
alphaFade.setDuration(APPEAR_ANIM_LEN);
@@ -178,7 +176,11 @@
requestLayout(); // pick up any final changes in position
}
});
+
alphaFade.start();
+
+ mAppearingViews.put(child, alphaFade);
+
requestLayout(); // start the container animation
}
}
@@ -187,27 +189,27 @@
public void removeView(View child) {
final View childF = child;
if (ANIMATE_LAYOUT) {
- if (mAppearingViews.contains(child)) {
+ if (mAppearingViews.containsKey(child)) {
mAppearingViews.remove(child);
}
- mDisappearingViews.add(child);
-
child.setPivotY(0);
final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f);
+ alphaFade.setDuration(DISAPPEAR_ANIM_LEN);
alphaFade.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "actually removing child: " + childF);
NotificationRowLayout.super.removeView(childF);
- childF.setAlpha(1f);
mDisappearingViews.remove(childF);
requestLayout(); // pick up any final changes in position
}
});
- alphaFade.setDuration(DISAPPEAR_ANIM_LEN);
alphaFade.start();
+
+ mDisappearingViews.put(child, alphaFade);
+
requestLayout(); // start the container animation
} else {
super.removeView(child);
@@ -246,7 +248,7 @@
if (child.getVisibility() == GONE) {
continue;
}
- if (mDisappearingViews.contains(child)) {
+ if (mDisappearingViews.containsKey(child)) {
continue;
}
numRows++;
@@ -304,14 +306,19 @@
if (child.getVisibility() == GONE) {
continue;
}
- float alpha = child.getAlpha();
- if (alpha > 1.0f) {
- if (DEBUG) {
- Slog.w(TAG, "alpha=" + alpha + " > 1!!! " + child);
- }
- alpha = 1f;
+ float progress = 1.0f;
+ if (mDisappearingViews.containsKey(child)) {
+ progress = 1.0f - mDisappearingViews.get(child).getAnimatedFraction();
+ } else if (mAppearingViews.containsKey(child)) {
+ progress = 1.0f - mAppearingViews.get(child).getAnimatedFraction();
}
- final int thisRowHeight = (int)(alpha * mRowHeight);
+ if (progress > 1.0f) {
+ if (DEBUG) {
+ Slog.w(TAG, "progress=" + progress + " > 1!!! " + child);
+ }
+ progress = 1f;
+ }
+ final int thisRowHeight = (int)(progress * mRowHeight);
if (DEBUG) {
Slog.d(TAG, String.format(
"laying out child #%d: (0, %d, %d, %d) h=%d",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index b4c480b..55e4227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -301,6 +301,7 @@
mRecentsPanel = (RecentsPanelView) View.inflate(context,
R.layout.status_bar_recent_panel, null);
mRecentsPanel.setVisibility(View.GONE);
+ mRecentsPanel.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL,
mRecentsPanel));
mStatusBarView.setIgnoreChildren(2, mRecentButton, mRecentsPanel);
@@ -705,7 +706,6 @@
case MSG_OPEN_RECENTS_PANEL:
if (DEBUG) Slog.d(TAG, "opening recents panel");
if (mRecentsPanel != null) {
- disable(StatusBarManager.DISABLE_BACK);
mRecentsPanel.setVisibility(View.VISIBLE);
mRecentsPanel.show(true, true);
}
@@ -713,7 +713,6 @@
case MSG_CLOSE_RECENTS_PANEL:
if (DEBUG) Slog.d(TAG, "closing recents panel");
if (mRecentsPanel != null && mRecentsPanel.isShowing()) {
- disable(StatusBarManager.DISABLE_NONE);
mRecentsPanel.show(false, true);
}
break;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 2a34f18..64a9677 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -1202,12 +1202,15 @@
// showing secure lockscreen; disable expanding.
flags |= StatusBarManager.DISABLE_EXPAND;
}
+ if (isSecure()) {
+ // showing secure lockscreen; disable ticker.
+ flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
+ }
}
if (DEBUG) {
- Log.d(TAG,
- "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden
- + " isSecure=" + isSecure() + " --> flags=" + flags);
+ Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden
+ + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
}
mStatusBarManager.disable(flags);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index b69a7c2..f5e3a45 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -384,7 +384,7 @@
st.menu.stopDispatchingItemsChanged();
if ((cb == null) || !cb.onCreatePanelMenu(st.featureId, st.menu)) {
// Ditch the menu created above
- st.menu = null;
+ st.setMenu(null);
if (mActionBar != null) {
// Don't show it in the action bar either
@@ -3207,7 +3207,17 @@
}
void setMenu(MenuBuilder menu) {
+ if (menu == this.menu) return;
+
+ if (this.menu != null) {
+ this.menu.removeMenuPresenter(iconMenuPresenter);
+ this.menu.removeMenuPresenter(listMenuPresenter);
+ }
this.menu = menu;
+ if (menu != null) {
+ if (iconMenuPresenter != null) menu.addMenuPresenter(iconMenuPresenter);
+ if (listMenuPresenter != null) menu.addMenuPresenter(listMenuPresenter);
+ }
}
MenuView getListMenuView(MenuPresenter.Callback cb) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 997318a..be2ef82 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -163,6 +163,10 @@
private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
private static final int MSG_RUN_FULL_RESTORE = 10;
+ // backup task state machine tick
+ static final int MSG_BACKUP_RESTORE_STEP = 20;
+ static final int MSG_OP_COMPLETE = 21;
+
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -344,7 +348,16 @@
static final int OP_ACKNOWLEDGED = 1;
static final int OP_TIMEOUT = -1;
- final SparseIntArray mCurrentOperations = new SparseIntArray();
+ class Operation {
+ public int state;
+ public BackupRestoreTask callback;
+
+ Operation(int initialState, BackupRestoreTask callbackObj) {
+ state = initialState;
+ callback = callbackObj;
+ }
+ }
+ final SparseArray<Operation> mCurrentOperations = new SparseArray<Operation>();
final Object mCurrentOpLock = new Object();
final Random mTokenGenerator = new Random();
@@ -442,13 +455,16 @@
}
}
+ // At this point, we have started a new journal file, and the old
+ // file identity is being passed to the backup processing task.
+ // When it completes successfully, that old journal file will be
+ // deleted. If we crash prior to that, the old journal is parsed
+ // at next boot and the journaled requests fulfilled.
if (queue.size() > 0) {
- // At this point, we have started a new journal file, and the old
- // file identity is being passed to the backup processing thread.
- // When it completes successfully, that old journal file will be
- // deleted. If we crash prior to that, the old journal is parsed
- // at next boot and the journaled requests fulfilled.
- (new PerformBackupTask(transport, queue, oldJournal)).run();
+ // Spin up a backup state sequence and set it running
+ PerformBackupTask pbt = new PerformBackupTask(transport, queue, oldJournal);
+ Message pbtMessage = obtainMessage(MSG_BACKUP_RESTORE_STEP, pbt);
+ sendMessage(pbtMessage);
} else {
Slog.v(TAG, "Backup requested but nothing pending");
mWakelock.release();
@@ -456,6 +472,29 @@
break;
}
+ case MSG_BACKUP_RESTORE_STEP:
+ {
+ try {
+ BackupRestoreTask task = (BackupRestoreTask) msg.obj;
+ if (MORE_DEBUG) Slog.v(TAG, "Got next step for " + task + ", executing");
+ task.execute();
+ } catch (ClassCastException e) {
+ Slog.e(TAG, "Invalid backup task in flight, obj=" + msg.obj);
+ }
+ break;
+ }
+
+ case MSG_OP_COMPLETE:
+ {
+ try {
+ BackupRestoreTask task = (BackupRestoreTask) msg.obj;
+ task.operationComplete();
+ } catch (ClassCastException e) {
+ Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
+ }
+ break;
+ }
+
case MSG_RUN_FULL_BACKUP:
{
FullBackupParams params = (FullBackupParams)msg.obj;
@@ -469,9 +508,12 @@
{
RestoreParams params = (RestoreParams)msg.obj;
Slog.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
- (new PerformRestoreTask(params.transport, params.observer,
+ PerformRestoreTask task = new PerformRestoreTask(
+ params.transport, params.observer,
params.token, params.pkgInfo, params.pmToken,
- params.needFullBackup, params.filterSet)).run();
+ params.needFullBackup, params.filterSet);
+ Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
+ sendMessage(restoreMsg);
break;
}
@@ -540,15 +582,7 @@
case MSG_TIMEOUT:
{
- synchronized (mCurrentOpLock) {
- final int token = msg.arg1;
- int state = mCurrentOperations.get(token, OP_TIMEOUT);
- if (state == OP_PENDING) {
- if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + token);
- mCurrentOperations.put(token, OP_TIMEOUT);
- }
- mCurrentOpLock.notifyAll();
- }
+ handleTimeout(msg.arg1, msg.obj);
break;
}
@@ -558,7 +592,7 @@
if (mActiveRestoreSession != null) {
// Client app left the restore session dangling. We know that it
// can't be in the middle of an actual restore operation because
- // those are executed serially on this same handler thread. Clean
+ // the timeout is suspended while a restore is in progress. Clean
// up now.
Slog.w(TAG, "Restore session timed out; aborting");
post(mActiveRestoreSession.new EndRestoreRunnable(
@@ -1113,12 +1147,14 @@
}
// Enqueue a new backup of every participant
- int N = mBackupParticipants.size();
- for (int i=0; i<N; i++) {
- int uid = mBackupParticipants.keyAt(i);
- HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
- for (ApplicationInfo app: participants) {
- dataChangedImpl(app.packageName);
+ synchronized (mBackupParticipants) {
+ int N = mBackupParticipants.size();
+ for (int i=0; i<N; i++) {
+ int uid = mBackupParticipants.keyAt(i);
+ HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+ for (ApplicationInfo app: participants) {
+ dataChangedImpl(app.packageName);
+ }
}
}
}
@@ -1588,50 +1624,120 @@
}
// -----
- // Utility methods used by the asynchronous-with-timeout backup/restore operations
- boolean waitUntilOperationComplete(int token) {
- int finalState = OP_PENDING;
+ // Interface and methods used by the asynchronous-with-timeout backup/restore operations
+
+ interface BackupRestoreTask {
+ // Execute one tick of whatever state machine the task implements
+ void execute();
+
+ // An operation that wanted a callback has completed
+ void operationComplete();
+
+ // An operation that wanted a callback has timed out
+ void handleTimeout();
+ }
+
+ void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback) {
+ if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ + " interval=" + interval);
synchronized (mCurrentOpLock) {
- try {
- while ((finalState = mCurrentOperations.get(token, OP_TIMEOUT)) == OP_PENDING) {
- try {
- mCurrentOpLock.wait();
- } catch (InterruptedException e) {}
+ mCurrentOperations.put(token, new Operation(OP_PENDING, callback));
+
+ Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0, callback);
+ mBackupHandler.sendMessageDelayed(msg, interval);
+ }
+ }
+
+ // synchronous waiter case
+ boolean waitUntilOperationComplete(int token) {
+ if (MORE_DEBUG) Slog.i(TAG, "Blocking until operation complete for "
+ + Integer.toHexString(token));
+ int finalState = OP_PENDING;
+ Operation op = null;
+ synchronized (mCurrentOpLock) {
+ while (true) {
+ op = mCurrentOperations.get(token);
+ if (op == null) {
+ // mysterious disappearance: treat as success with no callback
+ break;
+ } else {
+ if (op.state == OP_PENDING) {
+ try {
+ mCurrentOpLock.wait();
+ } catch (InterruptedException e) {}
+ // When the wait is notified we loop around and recheck the current state
+ } else {
+ // No longer pending; we're done
+ finalState = op.state;
+ break;
+ }
}
- } catch (IndexOutOfBoundsException e) {
- // the operation has been mysteriously cleared from our
- // bookkeeping -- consider this a success and ignore it.
}
}
+
mBackupHandler.removeMessages(MSG_TIMEOUT);
if (MORE_DEBUG) Slog.v(TAG, "operation " + Integer.toHexString(token)
+ " complete: finalState=" + finalState);
return finalState == OP_ACKNOWLEDGED;
}
- void prepareOperationTimeout(int token, long interval) {
- if (MORE_DEBUG) Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
- + " interval=" + interval);
+ void handleTimeout(int token, Object obj) {
+ // Notify any synchronous waiters
+ Operation op = null;
synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, OP_PENDING);
- Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
- mBackupHandler.sendMessageDelayed(msg, interval);
+ op = mCurrentOperations.get(token);
+ if (MORE_DEBUG) {
+ if (op == null) Slog.w(TAG, "Timeout of token " + Integer.toHexString(token)
+ + " but no op found");
+ }
+ int state = (op != null) ? op.state : OP_TIMEOUT;
+ if (state == OP_PENDING) {
+ if (DEBUG) Slog.v(TAG, "TIMEOUT: token=" + Integer.toHexString(token));
+ op.state = OP_TIMEOUT;
+ mCurrentOperations.put(token, op);
+ }
+ mCurrentOpLock.notifyAll();
+ }
+
+ // If there's a TimeoutHandler for this event, call it
+ if (op != null && op.callback != null) {
+ op.callback.handleTimeout();
}
}
// ----- Back up a set of applications via a worker thread -----
- class PerformBackupTask implements Runnable {
- private static final String TAG = "PerformBackupThread";
+ enum BackupState {
+ INITIAL,
+ RUNNING_QUEUE,
+ FINAL
+ }
+
+ class PerformBackupTask implements BackupRestoreTask {
+ private static final String TAG = "PerformBackupTask";
+
IBackupTransport mTransport;
ArrayList<BackupRequest> mQueue;
+ ArrayList<BackupRequest> mOriginalQueue;
File mStateDir;
File mJournal;
+ BackupState mCurrentState;
+
+ // carried information about the current in-flight operation
+ PackageInfo mCurrentPackage;
+ File mSavedStateName;
+ File mBackupDataName;
+ File mNewStateName;
+ ParcelFileDescriptor mSavedState;
+ ParcelFileDescriptor mBackupData;
+ ParcelFileDescriptor mNewState;
+ int mStatus;
+ boolean mFinished;
public PerformBackupTask(IBackupTransport transport, ArrayList<BackupRequest> queue,
File journal) {
mTransport = transport;
- mQueue = queue;
+ mOriginalQueue = queue;
mJournal = journal;
try {
@@ -1639,26 +1745,62 @@
} catch (RemoteException e) {
// can't happen; the transport is local
}
+
+ mCurrentState = BackupState.INITIAL;
+ mFinished = false;
}
- public void run() {
- int status = BackupConstants.TRANSPORT_OK;
- long startRealtime = SystemClock.elapsedRealtime();
+ // Main entry point: perform one chunk of work, updating the state as appropriate
+ // and reposting the next chunk to the primary backup handler thread.
+ @Override
+ public void execute() {
+ switch (mCurrentState) {
+ case INITIAL:
+ beginBackup();
+ break;
+
+ case RUNNING_QUEUE:
+ invokeNextAgent();
+ break;
+
+ case FINAL:
+ if (!mFinished) finalizeBackup();
+ else {
+ Slog.e(TAG, "Duplicate finish");
+ }
+ mFinished = true;
+ break;
+ }
+ }
+
+ // We're starting a backup pass. Initialize the transport and send
+ // the PM metadata blob if we haven't already.
+ void beginBackup() {
+ mStatus = BackupConstants.TRANSPORT_OK;
+
+ // Sanity check: if the queue is empty we have no work to do.
+ if (mOriginalQueue.isEmpty()) {
+ Slog.w(TAG, "Backup begun with an empty queue - nothing to do.");
+ return;
+ }
+
+ // We need to retain the original queue contents in case of transport
+ // failure, but we want a working copy that we can manipulate along
+ // the way.
+ mQueue = (ArrayList<BackupRequest>) mOriginalQueue.clone();
+
if (DEBUG) Slog.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
- // Backups run at background priority
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
+ File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
try {
EventLog.writeEvent(EventLogTags.BACKUP_START, mTransport.transportDirName());
// If we haven't stored package manager metadata yet, we must init the transport.
- File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
- if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
+ if (mStatus == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
Slog.i(TAG, "Initializing (wiping) backup state and transport storage");
resetBackupState(mStateDir); // Just to make sure.
- status = mTransport.initializeDevice();
- if (status == BackupConstants.TRANSPORT_OK) {
+ mStatus = mTransport.initializeDevice();
+ if (mStatus == BackupConstants.TRANSPORT_OK) {
EventLog.writeEvent(EventLogTags.BACKUP_INITIALIZE);
} else {
EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
@@ -1671,207 +1813,219 @@
// directly and use a synthetic BackupRequest. We always run this pass
// because it's cheap and this way we guarantee that we don't get out of
// step even if we're selecting among various transports at run time.
- if (status == BackupConstants.TRANSPORT_OK) {
+ if (mStatus == BackupConstants.TRANSPORT_OK) {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
mPackageManager, allAgentPackages());
- status = processOneBackup(PACKAGE_MANAGER_SENTINEL,
+ mStatus = invokeAgentForBackup(PACKAGE_MANAGER_SENTINEL,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
}
- if (status == BackupConstants.TRANSPORT_OK) {
- // Now run all the backups in our queue
- status = doQueuedBackups(mTransport);
- }
-
- if (status == BackupConstants.TRANSPORT_OK) {
- // Tell the transport to finish everything it has buffered
- status = mTransport.finishBackup();
- if (status == BackupConstants.TRANSPORT_OK) {
- int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
- EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, mQueue.size(), millis);
- } else {
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(finish)");
- Slog.e(TAG, "Transport error in finishBackup()");
- }
- }
-
- if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
- // The backend reports that our dataset has been wiped. We need to
- // reset all of our bookkeeping and instead run a new backup pass for
- // everything.
+ if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ // The backend reports that our dataset has been wiped. Note this in
+ // the event log; the no-success code below will reset the backup
+ // state as well.
EventLog.writeEvent(EventLogTags.BACKUP_RESET, mTransport.transportDirName());
- resetBackupState(mStateDir);
}
} catch (Exception e) {
Slog.e(TAG, "Error in backup thread", e);
- status = BackupConstants.TRANSPORT_ERROR;
+ mStatus = BackupConstants.TRANSPORT_ERROR;
} finally {
- // If everything actually went through and this is the first time we've
- // done a backup, we can now record what the current backup dataset token
- // is.
- if ((mCurrentToken == 0) && (status == BackupConstants.TRANSPORT_OK)) {
- try {
- mCurrentToken = mTransport.getCurrentRestoreSet();
- } catch (RemoteException e) { /* cannot happen */ }
- writeRestoreTokens();
+ // If we've succeeded so far, invokeAgentForBackup() will have run the PM
+ // metadata and its completion/timeout callback will continue the state
+ // machine chain. If it failed that won't happen; we handle that now.
+ if (mStatus != BackupConstants.TRANSPORT_OK) {
+ // if things went wrong at this point, we need to
+ // restage everything and try again later.
+ resetBackupState(mStateDir); // Just to make sure.
+ executeNextState(BackupState.FINAL);
}
-
- // If things went wrong, we need to re-stage the apps we had expected
- // to be backing up in this pass. This journals the package names in
- // the current active pending-backup file, not in the we are holding
- // here in mJournal.
- if (status != BackupConstants.TRANSPORT_OK) {
- Slog.w(TAG, "Backup pass unsuccessful, restaging");
- for (BackupRequest req : mQueue) {
- dataChangedImpl(req.packageName);
- }
-
- // We also want to reset the backup schedule based on whatever
- // the transport suggests by way of retry/backoff time.
- try {
- startBackupAlarmsLocked(mTransport.requestBackupTime());
- } catch (RemoteException e) { /* cannot happen */ }
- }
-
- // Either backup was successful, in which case we of course do not need
- // this pass's journal any more; or it failed, in which case we just
- // re-enqueued all of these packages in the current active journal.
- // Either way, we no longer need this pass's journal.
- if (mJournal != null && !mJournal.delete()) {
- Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
- }
-
- // Only once we're entirely finished do we release the wakelock
- if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
- backupNow();
- }
-
- mWakelock.release();
}
}
- private int doQueuedBackups(IBackupTransport transport) {
- for (BackupRequest request : mQueue) {
- Slog.d(TAG, "starting agent for backup of " + request);
+ // Transport has been initialized and the PM metadata submitted successfully
+ // if that was warranted. Now we process the single next thing in the queue.
+ void invokeNextAgent() {
+ mStatus = BackupConstants.TRANSPORT_OK;
- // Verify that the requested app exists; it might be something that
- // requested a backup but was then uninstalled. The request was
- // journalled and rather than tamper with the journal it's safer
- // to sanity-check here. This also gives us the classname of the
- // package's backup agent.
- PackageInfo pkg;
- try {
- pkg = mPackageManager.getPackageInfo(request.packageName, 0);
- } catch (NameNotFoundException e) {
- Slog.d(TAG, "Package does not exist; skipping");
- continue;
- }
+ // Sanity check that we have work to do. If not, skip to the end where
+ // we reestablish the wakelock invariants etc.
+ if (mQueue.isEmpty()) {
+ Slog.e(TAG, "Running queue but it's empty!");
+ executeNextState(BackupState.FINAL);
+ return;
+ }
+
+ // pop the entry we're going to process on this step
+ BackupRequest request = mQueue.get(0);
+ mQueue.remove(0);
+
+ Slog.d(TAG, "starting agent for backup of " + request);
+
+ // Verify that the requested app exists; it might be something that
+ // requested a backup but was then uninstalled. The request was
+ // journalled and rather than tamper with the journal it's safer
+ // to sanity-check here. This also gives us the classname of the
+ // package's backup agent.
+ try {
+ mCurrentPackage = mPackageManager.getPackageInfo(request.packageName,
+ PackageManager.GET_SIGNATURES);
IBackupAgent agent = null;
try {
- mWakelock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
- agent = bindToAgentSynchronous(pkg.applicationInfo,
+ mWakelock.setWorkSource(new WorkSource(mCurrentPackage.applicationInfo.uid));
+ agent = bindToAgentSynchronous(mCurrentPackage.applicationInfo,
IApplicationThread.BACKUP_MODE_INCREMENTAL);
if (agent != null) {
- int result = processOneBackup(request.packageName, agent, transport);
- if (result != BackupConstants.TRANSPORT_OK) return result;
+ mStatus = invokeAgentForBackup(request.packageName, agent, mTransport);
+ // at this point we'll either get a completion callback from the
+ // agent, or a timeout message on the main handler. either way, we're
+ // done here as long as we're successful so far.
+ } else {
+ // Timeout waiting for the agent
+ mStatus = BackupConstants.AGENT_ERROR;
}
} catch (SecurityException ex) {
// Try for the next one.
Slog.d(TAG, "error in bind/backup", ex);
- } finally {
- try { // unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(pkg.applicationInfo);
- } catch (RemoteException e) {}
+ mStatus = BackupConstants.AGENT_ERROR;
+ }
+ } catch (NameNotFoundException e) {
+ Slog.d(TAG, "Package does not exist; skipping");
+ } finally {
+ mWakelock.setWorkSource(null);
+
+ // If there was an agent error, no timeout/completion handling will occur.
+ // That means we need to deal with the next state ourselves.
+ if (mStatus != BackupConstants.TRANSPORT_OK) {
+ BackupState nextState = BackupState.RUNNING_QUEUE;
+
+ // An agent-level failure means we reenqueue this one agent for
+ // a later retry, but otherwise proceed normally.
+ if (mStatus == BackupConstants.AGENT_ERROR) {
+ if (MORE_DEBUG) Slog.i(TAG, "Agent failure for " + request.packageName
+ + " - restaging");
+ dataChangedImpl(request.packageName);
+ mStatus = BackupConstants.TRANSPORT_OK;
+ if (mQueue.isEmpty()) nextState = BackupState.FINAL;
+ } else if (mStatus != BackupConstants.TRANSPORT_OK) {
+ // Transport-level failure means we reenqueue everything
+ revertAndEndBackup();
+ nextState = BackupState.FINAL;
+ }
+
+ executeNextState(nextState);
}
}
-
- mWakelock.setWorkSource(null);
-
- return BackupConstants.TRANSPORT_OK;
}
- private int processOneBackup(String packageName, IBackupAgent agent,
+ void finalizeBackup() {
+ // Either backup was successful, in which case we of course do not need
+ // this pass's journal any more; or it failed, in which case we just
+ // re-enqueued all of these packages in the current active journal.
+ // Either way, we no longer need this pass's journal.
+ if (mJournal != null && !mJournal.delete()) {
+ Slog.e(TAG, "Unable to remove backup journal file " + mJournal);
+ }
+
+ // If everything actually went through and this is the first time we've
+ // done a backup, we can now record what the current backup dataset token
+ // is.
+ if ((mCurrentToken == 0) && (mStatus == BackupConstants.TRANSPORT_OK)) {
+ try {
+ mCurrentToken = mTransport.getCurrentRestoreSet();
+ } catch (RemoteException e) {} // can't happen
+ writeRestoreTokens();
+ }
+
+ // Set up the next backup pass
+ if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+ backupNow();
+ }
+
+ // Only once we're entirely finished do we release the wakelock
+ Slog.i(TAG, "Backup pass finished.");
+ mWakelock.release();
+ }
+
+ // Invoke an agent's doBackup() and start a timeout message spinning on the main
+ // handler in case it doesn't get back to us.
+ int invokeAgentForBackup(String packageName, IBackupAgent agent,
IBackupTransport transport) {
if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName);
- File savedStateName = new File(mStateDir, packageName);
- File backupDataName = new File(mDataDir, packageName + ".data");
- File newStateName = new File(mStateDir, packageName + ".new");
+ mSavedStateName = new File(mStateDir, packageName);
+ mBackupDataName = new File(mDataDir, packageName + ".data");
+ mNewStateName = new File(mStateDir, packageName + ".new");
- ParcelFileDescriptor savedState = null;
- ParcelFileDescriptor backupData = null;
- ParcelFileDescriptor newState = null;
+ mSavedState = null;
+ mBackupData = null;
+ mNewState = null;
- PackageInfo packInfo;
final int token = generateToken();
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
// be unraveled.
if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
- // The metadata 'package' is synthetic
- packInfo = new PackageInfo();
- packInfo.packageName = packageName;
- } else {
- packInfo = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES);
+ // The metadata 'package' is synthetic; construct one and make
+ // sure our global state is pointed at it
+ mCurrentPackage = new PackageInfo();
+ mCurrentPackage.packageName = packageName;
}
// In a full backup, we pass a null ParcelFileDescriptor as
// the saved-state "file". This is by definition an incremental,
// so we build a saved state file to pass.
- savedState = ParcelFileDescriptor.open(savedStateName,
+ mSavedState = ParcelFileDescriptor.open(mSavedStateName,
ParcelFileDescriptor.MODE_READ_ONLY |
ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
- backupData = ParcelFileDescriptor.open(backupDataName,
+ mBackupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
- newState = ParcelFileDescriptor.open(newStateName,
+ mNewState = ParcelFileDescriptor.open(mNewStateName,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
// Initiate the target's backup pass
- prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
- agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
- boolean success = waitUntilOperationComplete(token);
-
- if (!success) {
- // timeout -- bail out into the failed-transaction logic
- throw new RuntimeException("Backup timeout");
- }
-
- logBackupComplete(packageName);
- if (DEBUG) Slog.v(TAG, "doBackup() success");
+ prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, this);
+ agent.doBackup(mSavedState, mBackupData, mNewState, token, mBackupManagerBinder);
} catch (Exception e) {
- Slog.e(TAG, "Error backing up " + packageName, e);
- EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName, e.toString());
- backupDataName.delete();
- newStateName.delete();
- return BackupConstants.TRANSPORT_ERROR;
- } finally {
- try { if (savedState != null) savedState.close(); } catch (IOException e) {}
- try { if (backupData != null) backupData.close(); } catch (IOException e) {}
- try { if (newState != null) newState.close(); } catch (IOException e) {}
- savedState = backupData = newState = null;
- synchronized (mCurrentOpLock) {
- mCurrentOperations.clear();
- }
+ Slog.e(TAG, "Error invoking for backup on " + packageName);
+ EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName,
+ e.toString());
+ agentErrorCleanup();
+ return BackupConstants.AGENT_ERROR;
}
- // Now propagate the newly-backed-up data to the transport
- int result = BackupConstants.TRANSPORT_OK;
+ // At this point the agent is off and running. The next thing to happen will
+ // either be a callback from the agent, at which point we'll process its data
+ // for transport, or a timeout. Either way the next phase will happen in
+ // response to the TimeoutHandler interface callbacks.
+ return BackupConstants.TRANSPORT_OK;
+ }
+
+ @Override
+ public void operationComplete() {
+ // Okay, the agent successfully reported back to us. Spin the data off to the
+ // transport and proceed with the next stage.
+ if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
+ + mCurrentPackage.packageName);
+ mBackupHandler.removeMessages(MSG_TIMEOUT);
+ clearAgentState();
+
+ ParcelFileDescriptor backupData = null;
+ mStatus = BackupConstants.TRANSPORT_OK;
try {
- int size = (int) backupDataName.length();
+ int size = (int) mBackupDataName.length();
if (size > 0) {
- if (result == BackupConstants.TRANSPORT_OK) {
- backupData = ParcelFileDescriptor.open(backupDataName,
+ if (mStatus == BackupConstants.TRANSPORT_OK) {
+ backupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- result = transport.performBackup(packInfo, backupData);
+ mStatus = mTransport.performBackup(mCurrentPackage, backupData);
}
// TODO - We call finishBackup() for each application backed up, because
@@ -1879,8 +2033,8 @@
// hold off on finishBackup() until the end, which implies holding off on
// renaming *all* the output state files (see below) until that happens.
- if (result == BackupConstants.TRANSPORT_OK) {
- result = transport.finishBackup();
+ if (mStatus == BackupConstants.TRANSPORT_OK) {
+ mStatus = mTransport.finishBackup();
}
} else {
if (DEBUG) Slog.i(TAG, "no backup data written; not calling transport");
@@ -1889,22 +2043,102 @@
// After successful transport, delete the now-stale data
// and juggle the files so that next time we supply the agent
// with the new state file it just created.
- if (result == BackupConstants.TRANSPORT_OK) {
- backupDataName.delete();
- newStateName.renameTo(savedStateName);
- EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, packageName, size);
+ if (mStatus == BackupConstants.TRANSPORT_OK) {
+ mBackupDataName.delete();
+ mNewStateName.renameTo(mSavedStateName);
+ EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE,
+ mCurrentPackage.packageName, size);
+ logBackupComplete(mCurrentPackage.packageName);
} else {
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
+ mCurrentPackage.packageName);
}
} catch (Exception e) {
- Slog.e(TAG, "Transport error backing up " + packageName, e);
- EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
- result = BackupConstants.TRANSPORT_ERROR;
+ Slog.e(TAG, "Transport error backing up " + mCurrentPackage.packageName, e);
+ EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
+ mCurrentPackage.packageName);
+ mStatus = BackupConstants.TRANSPORT_ERROR;
} finally {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
}
- return result;
+ // If we encountered an error here it's a transport-level failure. That
+ // means we need to halt everything and reschedule everything for next time.
+ final BackupState nextState;
+ if (mStatus != BackupConstants.TRANSPORT_OK) {
+ revertAndEndBackup();
+ nextState = BackupState.FINAL;
+ } else {
+ // Success! Proceed with the next app if any, otherwise we're done.
+ nextState = (mQueue.isEmpty()) ? BackupState.FINAL : BackupState.RUNNING_QUEUE;
+ }
+
+ executeNextState(nextState);
+ }
+
+ @Override
+ public void handleTimeout() {
+ // Whoops, the current agent timed out running doBackup(). Tidy up and restage
+ // it for the next time we run a backup pass.
+ // !!! TODO: keep track of failure counts per agent, and blacklist those which
+ // fail repeatedly (i.e. have proved themselves to be buggy).
+ Slog.e(TAG, "Timeout backing up " + mCurrentPackage.packageName);
+ EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, mCurrentPackage.packageName,
+ "timeout");
+ agentErrorCleanup();
+ dataChangedImpl(mCurrentPackage.packageName);
+ }
+
+ void revertAndEndBackup() {
+ if (MORE_DEBUG) Slog.i(TAG, "Reverting backup queue - restaging everything");
+ for (BackupRequest request : mOriginalQueue) {
+ dataChangedImpl(request.packageName);
+ }
+ // We also want to reset the backup schedule based on whatever
+ // the transport suggests by way of retry/backoff time.
+ restartBackupAlarm();
+ }
+
+ void agentErrorCleanup() {
+ mBackupDataName.delete();
+ mNewStateName.delete();
+ clearAgentState();
+
+ executeNextState(mQueue.isEmpty() ? BackupState.FINAL : BackupState.RUNNING_QUEUE);
+ }
+
+ // Cleanup common to both success and failure cases
+ void clearAgentState() {
+ try { if (mSavedState != null) mSavedState.close(); } catch (IOException e) {}
+ try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
+ try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
+ mSavedState = mBackupData = mNewState = null;
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.clear();
+ }
+
+ // If this was a pseudopackage there's no associated Activity Manager state
+ if (mCurrentPackage.applicationInfo != null) {
+ try { // unbind even on timeout, just in case
+ mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
+ } catch (RemoteException e) {}
+ }
+ }
+
+ void restartBackupAlarm() {
+ synchronized (mQueueLock) {
+ try {
+ startBackupAlarmsLocked(mTransport.requestBackupTime());
+ } catch (RemoteException e) { /* cannot happen */ }
+ }
+ }
+
+ void executeNextState(BackupState nextState) {
+ if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
+ + this + " nextState=" + nextState);
+ mCurrentState = nextState;
+ Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
+ mBackupHandler.sendMessage(msg);
}
}
@@ -1959,7 +2193,7 @@
}
if (DEBUG) Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
- prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL);
+ prepareOperationTimeout(mToken, TIMEOUT_FULL_BACKUP_INTERVAL, null);
mAgent.doFullBackup(mPipe, mToken, mBackupManagerBinder);
} catch (IOException e) {
Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
@@ -2320,7 +2554,7 @@
sendOnBackupPackage("Shared storage");
final int token = generateToken();
- prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
+ prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL, null);
agent.doFullBackup(mOutputFile, token, mBackupManagerBinder);
if (!waitUntilOperationComplete(token)) {
Slog.e(TAG, "Full backup failed on shared storage");
@@ -2899,8 +3133,7 @@
try {
if (DEBUG) Slog.d(TAG, "Invoking agent to restore file "
+ info.path);
- prepareOperationTimeout(token,
- TIMEOUT_FULL_BACKUP_INTERVAL);
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, null);
// fire up the app's agent listening on the socket. If
// the agent is running in the system process we can't
// just invoke it asynchronously, so we provide a thread
@@ -3693,7 +3926,15 @@
return true;
}
- class PerformRestoreTask implements Runnable {
+ enum RestoreState {
+ INITIAL,
+ DOWNLOAD_DATA,
+ PM_METADATA,
+ RUNNING_QUEUE,
+ FINAL
+ }
+
+ class PerformRestoreTask implements BackupRestoreTask {
private IBackupTransport mTransport;
private IRestoreObserver mObserver;
private long mToken;
@@ -3702,6 +3943,21 @@
private int mPmToken;
private boolean mNeedFullBackup;
private HashSet<String> mFilterSet;
+ private long mStartRealtime;
+ private PackageManagerBackupAgent mPmAgent;
+ private List<PackageInfo> mAgentPackages;
+ private ArrayList<PackageInfo> mRestorePackages;
+ private RestoreState mCurrentState;
+ private int mCount;
+ private boolean mFinished;
+ private int mStatus;
+ private File mBackupDataName;
+ private File mNewStateName;
+ private File mSavedStateName;
+ private ParcelFileDescriptor mBackupData;
+ private ParcelFileDescriptor mNewState;
+ private PackageInfo mCurrentPackage;
+
class RestoreRequest {
public PackageInfo app;
@@ -3716,6 +3972,10 @@
PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
long restoreSetToken, PackageInfo targetPackage, int pmToken,
boolean needFullBackup, String[] filterSet) {
+ mCurrentState = RestoreState.INITIAL;
+ mFinished = false;
+ mPmAgent = null;
+
mTransport = transport;
mObserver = observer;
mToken = restoreSetToken;
@@ -3739,50 +3999,79 @@
}
}
- public void run() {
- long startRealtime = SystemClock.elapsedRealtime();
- if (DEBUG) Slog.v(TAG, "Beginning restore process mTransport=" + mTransport
- + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
- + " mTargetPackage=" + mTargetPackage + " mFilterSet=" + mFilterSet
- + " mPmToken=" + mPmToken);
+ // Execute one tick of whatever state machine the task implements
+ @Override
+ public void execute() {
+ if (MORE_DEBUG) Slog.v(TAG, "*** Executing restore step: " + mCurrentState);
+ switch (mCurrentState) {
+ case INITIAL:
+ beginRestore();
+ break;
- PackageManagerBackupAgent pmAgent = null;
- int error = -1; // assume error
+ case DOWNLOAD_DATA:
+ downloadRestoreData();
+ break;
- // build the set of apps to restore
+ case PM_METADATA:
+ restorePmMetadata();
+ break;
+
+ case RUNNING_QUEUE:
+ restoreNextAgent();
+ break;
+
+ case FINAL:
+ if (!mFinished) finalizeRestore();
+ else {
+ Slog.e(TAG, "Duplicate finish");
+ }
+ mFinished = true;
+ break;
+ }
+ }
+
+ // Initialize and set up for the PM metadata restore, which comes first
+ void beginRestore() {
+ // Don't account time doing the restore as inactivity of the app
+ // that has opened a restore session.
+ mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+
+ // Assume error until we successfully init everything
+ mStatus = BackupConstants.TRANSPORT_ERROR;
+
try {
// TODO: Log this before getAvailableRestoreSets, somehow
EventLog.writeEvent(EventLogTags.RESTORE_START, mTransport.transportDirName(), mToken);
// Get the list of all packages which have backup enabled.
// (Include the Package Manager metadata pseudo-package first.)
- ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
+ mRestorePackages = new ArrayList<PackageInfo>();
PackageInfo omPackage = new PackageInfo();
omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
- restorePackages.add(omPackage);
+ mRestorePackages.add(omPackage);
- List<PackageInfo> agentPackages = allAgentPackages();
+ mAgentPackages = allAgentPackages();
if (mTargetPackage == null) {
// if there's a filter set, strip out anything that isn't
// present before proceeding
if (mFilterSet != null) {
- for (int i = agentPackages.size() - 1; i >= 0; i--) {
- final PackageInfo pkg = agentPackages.get(i);
+ for (int i = mAgentPackages.size() - 1; i >= 0; i--) {
+ final PackageInfo pkg = mAgentPackages.get(i);
if (! mFilterSet.contains(pkg.packageName)) {
- agentPackages.remove(i);
+ mAgentPackages.remove(i);
}
}
- if (DEBUG) {
+ if (MORE_DEBUG) {
Slog.i(TAG, "Post-filter package set for restore:");
- for (PackageInfo p : agentPackages) {
+ for (PackageInfo p : mAgentPackages) {
Slog.i(TAG, " " + p);
}
}
}
- restorePackages.addAll(agentPackages);
+ mRestorePackages.addAll(mAgentPackages);
} else {
// Just one package to attempt restore of
- restorePackages.add(mTargetPackage);
+ mRestorePackages.add(mTargetPackage);
}
// let the observer know that we're running
@@ -3790,306 +4079,412 @@
try {
// !!! TODO: get an actual count from the transport after
// its startRestore() runs?
- mObserver.restoreStarting(restorePackages.size());
+ mObserver.restoreStarting(mRestorePackages.size());
} catch (RemoteException e) {
Slog.d(TAG, "Restore observer died at restoreStarting");
mObserver = null;
}
}
+ } catch (RemoteException e) {
+ // Something has gone catastrophically wrong with the transport
+ Slog.e(TAG, "Error communicating with transport for restore");
+ executeNextState(RestoreState.FINAL);
+ return;
+ }
- if (mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0])) !=
- BackupConstants.TRANSPORT_OK) {
+ mStatus = BackupConstants.TRANSPORT_OK;
+ executeNextState(RestoreState.DOWNLOAD_DATA);
+ }
+
+ void downloadRestoreData() {
+ // Note that the download phase can be very time consuming, but we're executing
+ // it inline here on the looper. This is "okay" because it is not calling out to
+ // third party code; the transport is "trusted," and so we assume it is being a
+ // good citizen and timing out etc when appropriate.
+ //
+ // TODO: when appropriate, move the download off the looper and rearrange the
+ // error handling around that.
+ try {
+ mStatus = mTransport.startRestore(mToken,
+ mRestorePackages.toArray(new PackageInfo[0]));
+ if (mStatus != BackupConstants.TRANSPORT_OK) {
Slog.e(TAG, "Error starting restore operation");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ executeNextState(RestoreState.FINAL);
return;
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error communicating with transport for restore");
+ EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ mStatus = BackupConstants.TRANSPORT_ERROR;
+ executeNextState(RestoreState.FINAL);
+ return;
+ }
+ // Successful download of the data to be parceled out to the apps, so off we go.
+ executeNextState(RestoreState.PM_METADATA);
+ }
+
+ void restorePmMetadata() {
+ try {
String packageName = mTransport.nextRestorePackage();
if (packageName == null) {
Slog.e(TAG, "Error getting first restore package");
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ mStatus = BackupConstants.TRANSPORT_ERROR;
+ executeNextState(RestoreState.FINAL);
return;
} else if (packageName.equals("")) {
Slog.i(TAG, "No restore data available");
- int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, 0, millis);
+ mStatus = BackupConstants.TRANSPORT_OK;
+ executeNextState(RestoreState.FINAL);
return;
} else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Slog.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
- + "\", found only \"" + packageName + "\"");
+ + "\", found only \"" + packageName + "\"");
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
"Package manager data missing");
+ executeNextState(RestoreState.FINAL);
return;
}
// Pull the Package Manager metadata from the restore set first
- pmAgent = new PackageManagerBackupAgent(
- mPackageManager, agentPackages);
- processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+ PackageInfo omPackage = new PackageInfo();
+ omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ mPmAgent = new PackageManagerBackupAgent(
+ mPackageManager, mAgentPackages);
+ initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()),
mNeedFullBackup);
+ // The PM agent called operationComplete() already, because our invocation
+ // of it is process-local and therefore synchronous. That means that a
+ // RUNNING_QUEUE message is already enqueued. Only if we're unable to
+ // proceed with running the queue do we remove that pending message and
+ // jump straight to the FINAL state.
// Verify that the backup set includes metadata. If not, we can't do
// signature/version verification etc, so we simply do not proceed with
// the restore operation.
- if (!pmAgent.hasMetadata()) {
+ if (!mPmAgent.hasMetadata()) {
Slog.e(TAG, "No restore metadata available, so not restoring settings");
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, PACKAGE_MANAGER_SENTINEL,
- "Package manager restore metadata missing");
+ "Package manager restore metadata missing");
+ mStatus = BackupConstants.TRANSPORT_ERROR;
+ mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
+ executeNextState(RestoreState.FINAL);
return;
}
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error communicating with transport for restore");
+ EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ mStatus = BackupConstants.TRANSPORT_ERROR;
+ mBackupHandler.removeMessages(MSG_BACKUP_RESTORE_STEP, this);
+ executeNextState(RestoreState.FINAL);
+ return;
+ }
- int count = 0;
- for (;;) {
- packageName = mTransport.nextRestorePackage();
+ // Metadata is intact, so we can now run the restore queue. If we get here,
+ // we have already enqueued the necessary next-step message on the looper.
+ }
- if (packageName == null) {
- Slog.e(TAG, "Error getting next restore package");
- EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
- return;
- } else if (packageName.equals("")) {
- if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
- break;
- }
+ void restoreNextAgent() {
+ try {
+ String packageName = mTransport.nextRestorePackage();
- if (mObserver != null) {
- try {
- mObserver.onUpdate(count, packageName);
- } catch (RemoteException e) {
- Slog.d(TAG, "Restore observer died in onUpdate");
- mObserver = null;
- }
- }
-
- Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
- if (metaInfo == null) {
- Slog.e(TAG, "Missing metadata for " + packageName);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
- "Package metadata missing");
- continue;
- }
-
- PackageInfo packageInfo;
- try {
- int flags = PackageManager.GET_SIGNATURES;
- packageInfo = mPackageManager.getPackageInfo(packageName, flags);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Invalid package restoring data", e);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
- "Package missing on device");
- continue;
- }
-
- if (metaInfo.versionCode > packageInfo.versionCode) {
- // Data is from a "newer" version of the app than we have currently
- // installed. If the app has not declared that it is prepared to
- // handle this case, we do not attempt the restore.
- if ((packageInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
- String message = "Version " + metaInfo.versionCode
- + " > installed version " + packageInfo.versionCode;
- Slog.w(TAG, "Package " + packageName + ": " + message);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
- packageName, message);
- continue;
- } else {
- if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
- + " > installed " + packageInfo.versionCode
- + " but restoreAnyVersion");
- }
- }
-
- if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
- Slog.w(TAG, "Signature mismatch restoring " + packageName);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
- "Signature mismatch");
- continue;
- }
-
- if (DEBUG) Slog.v(TAG, "Package " + packageName
- + " restore version [" + metaInfo.versionCode
- + "] is compatible with installed version ["
- + packageInfo.versionCode + "]");
-
- // Then set up and bind the agent
- IBackupAgent agent = bindToAgentSynchronous(
- packageInfo.applicationInfo,
- IApplicationThread.BACKUP_MODE_INCREMENTAL);
- if (agent == null) {
- Slog.w(TAG, "Can't find backup agent for " + packageName);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
- "Restore agent missing");
- continue;
- }
-
- // And then finally run the restore on this agent
- try {
- processOneRestore(packageInfo, metaInfo.versionCode, agent,
- mNeedFullBackup);
- ++count;
- } finally {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
-
- // The agent was probably running with a stub Application object,
- // which isn't a valid run mode for the main app logic. Shut
- // down the app so that next time it's launched, it gets the
- // usual full initialization. Note that this is only done for
- // full-system restores: when a single app has requested a restore,
- // it is explicitly not killed following that operation.
- if (mTargetPackage == null && (packageInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
- if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
- + packageInfo.applicationInfo.processName);
- mActivityManager.killApplicationProcess(
- packageInfo.applicationInfo.processName,
- packageInfo.applicationInfo.uid);
- }
- }
- }
-
- // if we get this far, report success to the observer
- error = 0;
- int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
- EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, count, millis);
- } catch (Exception e) {
- Slog.e(TAG, "Error in restore thread", e);
- } finally {
- if (DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
-
- try {
- mTransport.finishRestore();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error finishing restore", e);
+ if (packageName == null) {
+ Slog.e(TAG, "Error getting next restore package");
+ EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
+ executeNextState(RestoreState.FINAL);
+ return;
+ } else if (packageName.equals("")) {
+ if (DEBUG) Slog.v(TAG, "No next package, finishing restore");
+ int millis = (int) (SystemClock.elapsedRealtime() - mStartRealtime);
+ EventLog.writeEvent(EventLogTags.RESTORE_SUCCESS, mCount, millis);
+ executeNextState(RestoreState.FINAL);
+ return;
}
if (mObserver != null) {
try {
- mObserver.restoreFinished(error);
+ mObserver.onUpdate(mCount, packageName);
} catch (RemoteException e) {
- Slog.d(TAG, "Restore observer died at restoreFinished");
+ Slog.d(TAG, "Restore observer died in onUpdate");
+ mObserver = null;
}
}
- // If this was a restoreAll operation, record that this was our
- // ancestral dataset, as well as the set of apps that are possibly
- // restoreable from the dataset
- if (mTargetPackage == null && pmAgent != null) {
- mAncestralPackages = pmAgent.getRestoredPackages();
- mAncestralToken = mToken;
- writeRestoreTokens();
+ Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
+ if (metaInfo == null) {
+ Slog.e(TAG, "Missing metadata for " + packageName);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
+ "Package metadata missing");
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ return;
}
- // We must under all circumstances tell the Package Manager to
- // proceed with install notifications if it's waiting for us.
- if (mPmToken > 0) {
- if (DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
- try {
- mPackageManagerBinder.finishPackageInstall(mPmToken);
- } catch (RemoteException e) { /* can't happen */ }
+ PackageInfo packageInfo;
+ try {
+ int flags = PackageManager.GET_SIGNATURES;
+ packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Invalid package restoring data", e);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
+ "Package missing on device");
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ return;
}
- // Furthermore we need to reset the session timeout clock
- mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
- mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
- TIMEOUT_RESTORE_INTERVAL);
+ if (metaInfo.versionCode > packageInfo.versionCode) {
+ // Data is from a "newer" version of the app than we have currently
+ // installed. If the app has not declared that it is prepared to
+ // handle this case, we do not attempt the restore.
+ if ((packageInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
+ String message = "Version " + metaInfo.versionCode
+ + " > installed version " + packageInfo.versionCode;
+ Slog.w(TAG, "Package " + packageName + ": " + message);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
+ packageName, message);
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ return;
+ } else {
+ if (DEBUG) Slog.v(TAG, "Version " + metaInfo.versionCode
+ + " > installed " + packageInfo.versionCode
+ + " but restoreAnyVersion");
+ }
+ }
- // done; we can finally release the wakelock
- mWakelock.release();
+ if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
+ Slog.w(TAG, "Signature mismatch restoring " + packageName);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
+ "Signature mismatch");
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ return;
+ }
+
+ if (DEBUG) Slog.v(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is compatible with installed version ["
+ + packageInfo.versionCode + "]");
+
+ // Then set up and bind the agent
+ IBackupAgent agent = bindToAgentSynchronous(
+ packageInfo.applicationInfo,
+ IApplicationThread.BACKUP_MODE_INCREMENTAL);
+ if (agent == null) {
+ Slog.w(TAG, "Can't find backup agent for " + packageName);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
+ "Restore agent missing");
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ return;
+ }
+
+ // And then finally start the restore on this agent
+ try {
+ initiateOneRestore(packageInfo, metaInfo.versionCode, agent, mNeedFullBackup);
+ ++mCount;
+ } catch (Exception e) {
+ Slog.e(TAG, "Error when attempting restore: " + e.toString());
+ agentErrorCleanup();
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to fetch restore data from transport");
+ mStatus = BackupConstants.TRANSPORT_ERROR;
+ executeNextState(RestoreState.FINAL);
}
}
- // Do the guts of a restore of one application, using mTransport.getRestoreData().
- void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
+ void finalizeRestore() {
+ if (MORE_DEBUG) Slog.d(TAG, "finishing restore mObserver=" + mObserver);
+
+ try {
+ mTransport.finishRestore();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error finishing restore", e);
+ }
+
+ if (mObserver != null) {
+ try {
+ mObserver.restoreFinished(mStatus);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Restore observer died at restoreFinished");
+ }
+ }
+
+ // If this was a restoreAll operation, record that this was our
+ // ancestral dataset, as well as the set of apps that are possibly
+ // restoreable from the dataset
+ if (mTargetPackage == null && mPmAgent != null) {
+ mAncestralPackages = mPmAgent.getRestoredPackages();
+ mAncestralToken = mToken;
+ writeRestoreTokens();
+ }
+
+ // We must under all circumstances tell the Package Manager to
+ // proceed with install notifications if it's waiting for us.
+ if (mPmToken > 0) {
+ if (MORE_DEBUG) Slog.v(TAG, "finishing PM token " + mPmToken);
+ try {
+ mPackageManagerBinder.finishPackageInstall(mPmToken);
+ } catch (RemoteException e) { /* can't happen */ }
+ }
+
+ // Furthermore we need to reset the session timeout clock
+ mBackupHandler.removeMessages(MSG_RESTORE_TIMEOUT);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
+ TIMEOUT_RESTORE_INTERVAL);
+
+ // done; we can finally release the wakelock
+ Slog.i(TAG, "Restore complete.");
+ mWakelock.release();
+ }
+
+ // Call asynchronously into the app, passing it the restore data. The next step
+ // after this is always a callback, either operationComplete() or handleTimeout().
+ void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
boolean needFullBackup) {
- // !!! TODO: actually run the restore through mTransport
+ mCurrentPackage = app;
final String packageName = app.packageName;
- if (DEBUG) Slog.d(TAG, "processOneRestore packageName=" + packageName);
+ if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
// !!! TODO: get the dirs from the transport
- File backupDataName = new File(mDataDir, packageName + ".restore");
- File newStateName = new File(mStateDir, packageName + ".new");
- File savedStateName = new File(mStateDir, packageName);
-
- ParcelFileDescriptor backupData = null;
- ParcelFileDescriptor newState = null;
+ mBackupDataName = new File(mDataDir, packageName + ".restore");
+ mNewStateName = new File(mStateDir, packageName + ".new");
+ mSavedStateName = new File(mStateDir, packageName);
final int token = generateToken();
try {
// Run the transport's restore pass
- backupData = ParcelFileDescriptor.open(backupDataName,
+ mBackupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
- if (mTransport.getRestoreData(backupData) != BackupConstants.TRANSPORT_OK) {
+ if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
Slog.e(TAG, "Error getting restore data for " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
return;
}
// Okay, we have the data. Now have the agent do the restore.
- backupData.close();
- backupData = ParcelFileDescriptor.open(backupDataName,
+ mBackupData.close();
+ mBackupData = ParcelFileDescriptor.open(mBackupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- newState = ParcelFileDescriptor.open(newStateName,
+ mNewState = ParcelFileDescriptor.open(mNewStateName,
ParcelFileDescriptor.MODE_READ_WRITE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE);
// Kick off the restore, checking for hung agents
- prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL);
- agent.doRestore(backupData, appVersionCode, newState, token, mBackupManagerBinder);
- boolean success = waitUntilOperationComplete(token);
-
- if (!success) {
- throw new RuntimeException("restore timeout");
- }
-
- // if everything went okay, remember the recorded state now
- //
- // !!! TODO: the restored data should be migrated on the server
- // side into the current dataset. In that case the new state file
- // we just created would reflect the data already extant in the
- // backend, so there'd be nothing more to do. Until that happens,
- // however, we need to make sure that we record the data to the
- // current backend dataset. (Yes, this means shipping the data over
- // the wire in both directions. That's bad, but consistency comes
- // first, then efficiency.) Once we introduce server-side data
- // migration to the newly-restored device's dataset, we will change
- // the following from a discard of the newly-written state to the
- // "correct" operation of renaming into the canonical state blob.
- newStateName.delete(); // TODO: remove; see above comment
- //newStateName.renameTo(savedStateName); // TODO: replace with this
-
- int size = (int) backupDataName.length();
- EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, packageName, size);
+ prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
+ agent.doRestore(mBackupData, appVersionCode, mNewState, token, mBackupManagerBinder);
} catch (Exception e) {
- Slog.e(TAG, "Error restoring data for " + packageName, e);
+ Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
+ agentErrorCleanup(); // clears any pending timeout messages as well
- // If the agent fails restore, it might have put the app's data
- // into an incoherent state. For consistency we wipe its data
- // again in this case before propagating the exception
- clearApplicationDataSynchronous(packageName);
- } finally {
- backupDataName.delete();
- try { if (backupData != null) backupData.close(); } catch (IOException e) {}
- try { if (newState != null) newState.close(); } catch (IOException e) {}
- backupData = newState = null;
- synchronized (mCurrentOperations) {
- mCurrentOperations.delete(token);
- }
+ // After a restore failure we go back to running the queue. If there
+ // are no more packages to be restored that will be handled by the
+ // next step.
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ }
+ }
- // If we know a priori that we'll need to perform a full post-restore backup
- // pass, clear the new state file data. This means we're discarding work that
- // was just done by the app's agent, but this way the agent doesn't need to
- // take any special action based on global device state.
- if (needFullBackup) {
- newStateName.delete();
+ void agentErrorCleanup() {
+ // If the agent fails restore, it might have put the app's data
+ // into an incoherent state. For consistency we wipe its data
+ // again in this case before continuing with normal teardown
+ clearApplicationDataSynchronous(mCurrentPackage.packageName);
+ agentCleanup();
+ }
+
+ void agentCleanup() {
+ mBackupDataName.delete();
+ try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
+ try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
+ mBackupData = mNewState = null;
+
+ // if everything went okay, remember the recorded state now
+ //
+ // !!! TODO: the restored data should be migrated on the server
+ // side into the current dataset. In that case the new state file
+ // we just created would reflect the data already extant in the
+ // backend, so there'd be nothing more to do. Until that happens,
+ // however, we need to make sure that we record the data to the
+ // current backend dataset. (Yes, this means shipping the data over
+ // the wire in both directions. That's bad, but consistency comes
+ // first, then efficiency.) Once we introduce server-side data
+ // migration to the newly-restored device's dataset, we will change
+ // the following from a discard of the newly-written state to the
+ // "correct" operation of renaming into the canonical state blob.
+ mNewStateName.delete(); // TODO: remove; see above comment
+ //mNewStateName.renameTo(mSavedStateName); // TODO: replace with this
+
+ // If this wasn't the PM pseudopackage, tear down the agent side
+ if (mCurrentPackage.applicationInfo != null) {
+ // unbind and tidy up even on timeout or failure
+ try {
+ mActivityManager.unbindBackupAgent(mCurrentPackage.applicationInfo);
+
+ // The agent was probably running with a stub Application object,
+ // which isn't a valid run mode for the main app logic. Shut
+ // down the app so that next time it's launched, it gets the
+ // usual full initialization. Note that this is only done for
+ // full-system restores: when a single app has requested a restore,
+ // it is explicitly not killed following that operation.
+ if (mTargetPackage == null && (mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
+ if (DEBUG) Slog.d(TAG, "Restore complete, killing host process of "
+ + mCurrentPackage.applicationInfo.processName);
+ mActivityManager.killApplicationProcess(
+ mCurrentPackage.applicationInfo.processName,
+ mCurrentPackage.applicationInfo.uid);
+ }
+ } catch (RemoteException e) {
+ // can't happen; we run in the same process as the activity manager
}
}
+
+ // The caller is responsible for reestablishing the state machine; our
+ // responsibility here is to clear the decks for whatever comes next.
+ mBackupHandler.removeMessages(MSG_TIMEOUT, this);
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.clear();
+ }
+ }
+
+ // A call to agent.doRestore() has been positively acknowledged as complete
+ @Override
+ public void operationComplete() {
+ int size = (int) mBackupDataName.length();
+ EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
+ // Just go back to running the restore queue
+ agentCleanup();
+
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ }
+
+ // A call to agent.doRestore() has timed out
+ @Override
+ public void handleTimeout() {
+ Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
+ mCurrentPackage.packageName, "restore timeout");
+ // Handle like an agent that threw on invocation: wipe it and go on to the next
+ agentErrorCleanup();
+ executeNextState(RestoreState.RUNNING_QUEUE);
+ }
+
+ void executeNextState(RestoreState nextState) {
+ if (MORE_DEBUG) Slog.i(TAG, " => executing next step on "
+ + this + " nextState=" + nextState);
+ mCurrentState = nextState;
+ Message msg = mBackupHandler.obtainMessage(MSG_BACKUP_RESTORE_STEP, this);
+ mBackupHandler.sendMessage(msg);
}
}
@@ -4884,12 +5279,23 @@
// Note that a currently-active backup agent has notified us that it has
// completed the given outstanding asynchronous backup/restore operation.
+ @Override
public void opComplete(int token) {
+ if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
+ Operation op = null;
synchronized (mCurrentOpLock) {
- if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
- mCurrentOperations.put(token, OP_ACKNOWLEDGED);
+ op = mCurrentOperations.get(token);
+ if (op != null) {
+ op.state = OP_ACKNOWLEDGED;
+ }
mCurrentOpLock.notifyAll();
}
+
+ // The completion callback, if any, is invoked on the handler
+ if (op != null && op.callback != null) {
+ Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
+ mBackupHandler.sendMessage(msg);
+ }
}
// ----- Restore session -----
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 1497511..f774dea 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -98,6 +98,7 @@
public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
/** {@link #mStatsXtUid} headers. */
+ private static final String KEY_IDX = "idx";
private static final String KEY_IFACE = "iface";
private static final String KEY_UID = "uid_tag_int";
private static final String KEY_COUNTER_SET = "cnt_set";
@@ -1323,17 +1324,20 @@
// TODO: remove knownLines check once 5087722 verified
final HashSet<String> knownLines = Sets.newHashSet();
+ // TODO: remove lastIdx check once 5270106 verified
+ int lastIdx = 0;
final ArrayList<String> keys = Lists.newArrayList();
final ArrayList<String> values = Lists.newArrayList();
final HashMap<String, String> parsed = Maps.newHashMap();
BufferedReader reader = null;
+ String line = null;
try {
reader = new BufferedReader(new FileReader(mStatsXtUid));
// parse first line as header
- String line = reader.readLine();
+ line = reader.readLine();
splitLine(line, keys);
// parse remaining lines
@@ -1342,32 +1346,35 @@
parseLine(keys, values, parsed);
if (!knownLines.add(line)) {
- throw new IllegalStateException("encountered duplicate proc entry");
+ throw new IllegalStateException("duplicate proc entry: " + line);
}
- try {
- entry.iface = parsed.get(KEY_IFACE);
- entry.uid = getParsedInt(parsed, KEY_UID);
- entry.set = getParsedInt(parsed, KEY_COUNTER_SET);
- entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX));
- entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES);
- entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS);
- entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES);
- entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS);
+ final int idx = getParsedInt(parsed, KEY_IDX);
+ if (idx > lastIdx + 1) {
+ throw new IllegalStateException(
+ "inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
+ }
+ lastIdx = idx;
- if (limitUid == UID_ALL || limitUid == entry.uid) {
- stats.addValues(entry);
- }
- } catch (NumberFormatException e) {
- Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
+ entry.iface = parsed.get(KEY_IFACE);
+ entry.uid = getParsedInt(parsed, KEY_UID);
+ entry.set = getParsedInt(parsed, KEY_COUNTER_SET);
+ entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX));
+ entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES);
+ entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS);
+ entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES);
+ entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS);
+
+ if (limitUid == UID_ALL || limitUid == entry.uid) {
+ stats.addValues(entry);
}
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new IllegalStateException("problem parsing line: " + line, e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new IllegalStateException("problem parsing line: " + line, e);
} catch (IOException e) {
- throw new IllegalStateException("problem parsing stats: " + e);
+ throw new IllegalStateException("problem parsing line: " + line, e);
} finally {
IoUtils.closeQuietly(reader);
}
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index ca3b12d..bab9f8a 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -301,7 +301,7 @@
// also allows calls from window manager which is in this process.
enforceStatusBarService();
- if (SPEW) Slog.d(TAG, "setSystemUiVisibility(" + vis + ")");
+ if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
synchronized (mLock) {
updateUiVisibilityLocked(vis);
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 4447ad0..bc256ed 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -383,7 +383,7 @@
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities, int networkType) {
+ LinkCapabilities linkCapabilities, int networkType, boolean roaming) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -437,7 +437,7 @@
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- apnType, linkProperties, linkCapabilities);
+ apnType, linkProperties, linkCapabilities, roaming);
}
public void notifyDataConnectionFailed(String reason, String apnType) {
@@ -596,7 +596,7 @@
private void broadcastDataConnectionStateChanged(int state,
boolean isDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
- LinkCapabilities linkCapabilities) {
+ LinkCapabilities linkCapabilities, boolean roaming) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -618,6 +618,8 @@
if (linkCapabilities != null) {
intent.putExtra(Phone.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
}
+ if (roaming) intent.putExtra(Phone.DATA_NETWORK_ROAMING_KEY, true);
+
intent.putExtra(Phone.DATA_APN_KEY, apn);
intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
mContext.sendStickyBroadcast(intent);
diff --git a/services/java/com/android/server/WiredAccessoryObserver.java b/services/java/com/android/server/WiredAccessoryObserver.java
index e45c368..6a63eac 100644
--- a/services/java/com/android/server/WiredAccessoryObserver.java
+++ b/services/java/com/android/server/WiredAccessoryObserver.java
@@ -107,25 +107,18 @@
private synchronized final void updateState(String name, int state)
{
if (name.equals("usb_audio")) {
- if (state == 1) {
- switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
- BIT_USB_HEADSET_DGTL|BIT_HDMI_AUDIO)) |
- (state << 2));
- } else if (state == 2) {
- switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
- BIT_USB_HEADSET_ANLG|BIT_HDMI_AUDIO)) |
- (state << 3));
- } else switchState = (mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|BIT_HDMI_AUDIO));
- }
- else if (name.equals("hdmi")) {
+ switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|BIT_HDMI_AUDIO)) |
+ ((state == 1) ? BIT_USB_HEADSET_ANLG :
+ ((state == 2) ? BIT_USB_HEADSET_DGTL : 0)));
+ } else if (name.equals("hdmi")) {
switchState = ((mHeadsetState & (BIT_HEADSET|BIT_HEADSET_NO_MIC|
BIT_USB_HEADSET_DGTL|BIT_USB_HEADSET_ANLG)) |
- (state << 4));
- }
- else {
+ ((state == 1) ? BIT_HDMI_AUDIO : 0));
+ } else {
switchState = ((mHeadsetState & (BIT_HDMI_AUDIO|BIT_USB_HEADSET_ANLG|
BIT_USB_HEADSET_DGTL)) |
- state);
+ ((state == 1) ? BIT_HEADSET :
+ ((state == 2) ? BIT_HEADSET_NO_MIC : 0)));
}
update(name, switchState);
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index eb399ad..aa43bb6 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -203,6 +203,7 @@
}
case 1: {
mSendHoverDelayed.remove();
+ mPerformLongPressDelayed.remove();
// Send a hover for every finger down so the user gets feedback.
final int pointerId = pointerTracker.getPrimaryActivePointerId();
final int pointerIdBits = (1 << pointerId);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 9db56ce..d6d3b9d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -106,6 +106,7 @@
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.EventLog;
+import android.util.Pair;
import android.util.Slog;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -5004,7 +5005,13 @@
maxNum < N ? maxNum : N);
for (int i=0; i<N && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
- if (((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
+ // Return the entry if desired by the caller. We always return
+ // the first entry, because callers always expect this to be the
+ // forground app. We may filter others if the caller has
+ // not supplied RECENT_WITH_EXCLUDED and there is some reason
+ // we should exclude the entry.
+ if (i == 0
+ || ((flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0)
|| (tr.intent == null)
|| ((tr.intent.getFlags()
&Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0)) {
@@ -5152,6 +5159,29 @@
cleanUpRemovedTaskLocked(r,
(flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0);
return true;
+ } else {
+ TaskRecord tr = null;
+ int i=0;
+ while (i < mRecentTasks.size()) {
+ TaskRecord t = mRecentTasks.get(i);
+ if (t.taskId == taskId) {
+ tr = t;
+ break;
+ }
+ i++;
+ }
+ if (tr != null) {
+ if (tr.numActivities <= 0) {
+ // Caller is just removing a recent task that is
+ // not actively running. That is easy!
+ mRecentTasks.remove(i);
+ } else {
+ Slog.w(TAG, "removeTask: task " + taskId
+ + " does not have activities to remove, "
+ + " but numActivities=" + tr.numActivities
+ + ": " + tr);
+ }
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -7546,7 +7576,33 @@
return errList;
}
-
+
+ static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
+ if (adj >= ProcessList.EMPTY_APP_ADJ) {
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
+ } else if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
+ if (currApp != null) {
+ currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
+ }
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+ } else if (adj >= ProcessList.HOME_APP_ADJ) {
+ if (currApp != null) {
+ currApp.lru = 0;
+ }
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
+ } else if (adj >= ProcessList.SECONDARY_SERVER_ADJ) {
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
+ } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
+ } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
+ } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ } else {
+ return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+ }
+ }
+
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
// Lazy instantiation of list
List<ActivityManager.RunningAppProcessInfo> runList = null;
@@ -7567,28 +7623,12 @@
currApp.flags |= ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT;
}
int adj = app.curAdj;
- if (adj >= ProcessList.EMPTY_APP_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
- } else if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
- } else if (adj >= ProcessList.HOME_APP_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- currApp.lru = 0;
- } else if (adj >= ProcessList.SECONDARY_SERVER_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
- } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
- } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
- } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
- } else {
- currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
- }
+ currApp.importance = oomAdjToImportance(adj, currApp);
currApp.importanceReasonCode = app.adjTypeCode;
if (app.adjSource instanceof ProcessRecord) {
currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+ currApp.importanceReasonImportance = oomAdjToImportance(
+ app.adjSourceOom, null);
} else if (app.adjSource instanceof ActivityRecord) {
ActivityRecord r = (ActivityRecord)app.adjSource;
if (r.app != null) currApp.importanceReasonPid = r.app.pid;
@@ -7891,7 +7931,7 @@
if (mLruProcesses.size() > 0) {
if (needSep) pw.println(" ");
needSep = true;
- pw.println(" Process LRU list (most recent first):");
+ pw.println(" Process LRU list (sorted by oom_adj):");
dumpProcessOomList(pw, this, mLruProcesses, " ",
"Proc", "PERS", false);
needSep = true;
@@ -8069,29 +8109,6 @@
boolean needSep = false;
if (mLruProcesses.size() > 0) {
- ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mLruProcesses);
-
- Comparator<ProcessRecord> comparator = new Comparator<ProcessRecord>() {
- @Override
- public int compare(ProcessRecord object1, ProcessRecord object2) {
- if (object1.setAdj != object2.setAdj) {
- return object1.setAdj > object2.setAdj ? -1 : 1;
- }
- if (object1.setSchedGroup != object2.setSchedGroup) {
- return object1.setSchedGroup > object2.setSchedGroup ? -1 : 1;
- }
- if (object1.keeping != object2.keeping) {
- return object1.keeping ? -1 : 1;
- }
- if (object1.pid != object2.pid) {
- return object1.pid > object2.pid ? -1 : 1;
- }
- return 0;
- }
- };
-
- Collections.sort(procs, comparator);
-
if (needSep) pw.println(" ");
needSep = true;
pw.println(" OOM levels:");
@@ -8110,7 +8127,7 @@
if (needSep) pw.println(" ");
needSep = true;
pw.println(" Process OOM control:");
- dumpProcessOomList(pw, this, procs, " ",
+ dumpProcessOomList(pw, this, mLruProcesses, " ",
"Proc", "PERS", true);
needSep = true;
}
@@ -8859,10 +8876,33 @@
}
private static final void dumpProcessOomList(PrintWriter pw,
- ActivityManagerService service, List<ProcessRecord> list,
+ ActivityManagerService service, List<ProcessRecord> origList,
String prefix, String normalLabel, String persistentLabel,
boolean inclDetails) {
+ ArrayList<Pair<ProcessRecord, Integer>> list
+ = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size());
+ for (int i=0; i<origList.size(); i++) {
+ list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i));
+ }
+
+ Comparator<Pair<ProcessRecord, Integer>> comparator
+ = new Comparator<Pair<ProcessRecord, Integer>>() {
+ @Override
+ public int compare(Pair<ProcessRecord, Integer> object1,
+ Pair<ProcessRecord, Integer> object2) {
+ if (object1.first.setAdj != object2.first.setAdj) {
+ return object1.first.setAdj > object2.first.setAdj ? -1 : 1;
+ }
+ if (object1.second.intValue() != object2.second.intValue()) {
+ return object1.second.intValue() > object2.second.intValue() ? -1 : 1;
+ }
+ return 0;
+ }
+ };
+
+ Collections.sort(list, comparator);
+
final long curRealtime = SystemClock.elapsedRealtime();
final long realtimeSince = curRealtime - service.mLastPowerCheckRealtime;
final long curUptime = SystemClock.uptimeMillis();
@@ -8870,7 +8910,7 @@
final int N = list.size()-1;
for (int i=N; i>=0; i--) {
- ProcessRecord r = list.get(i);
+ ProcessRecord r = list.get(i).first;
String oomAdj;
if (r.setAdj >= ProcessList.EMPTY_APP_ADJ) {
oomAdj = buildOomTag("empty", null, r.setAdj, ProcessList.EMPTY_APP_ADJ);
@@ -8919,7 +8959,7 @@
}
pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)",
prefix, (r.persistent ? persistentLabel : normalLabel),
- N-i, oomAdj, schedGroup, foreground, r.trimMemoryLevel,
+ N-list.get(i).second, oomAdj, schedGroup, foreground, r.trimMemoryLevel,
r.toShortString(), r.adjType));
if (r.adjSource != null || r.adjTarget != null) {
pw.print(prefix);
@@ -13118,6 +13158,7 @@
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
app.adjSource = cr.binding.client;
+ app.adjSourceOom = clientAdj;
app.adjTarget = s.name;
}
if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
@@ -13140,6 +13181,7 @@
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE;
app.adjSource = a;
+ app.adjSourceOom = adj;
app.adjTarget = s.name;
}
}
@@ -13201,6 +13243,7 @@
app.adjTypeCode = ActivityManager.RunningAppProcessInfo
.REASON_PROVIDER_IN_USE;
app.adjSource = client;
+ app.adjSourceOom = clientAdj;
app.adjTarget = cpr.name;
}
if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
@@ -13511,16 +13554,21 @@
computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
if (app.curRawAdj != app.setRawAdj) {
- if (app.curRawAdj > ProcessList.FOREGROUND_APP_ADJ
- && app.setRawAdj <= ProcessList.FOREGROUND_APP_ADJ) {
- // If this app is transitioning from foreground to
- // non-foreground, have it do a gc.
- scheduleAppGcLocked(app);
- } else if (app.curRawAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
- && app.setRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
- // Likewise do a gc when an app is moving in to the
- // background (such as a service stopping).
- scheduleAppGcLocked(app);
+ if (false) {
+ // Removing for now. Forcing GCs is not so useful anymore
+ // with Dalvik, and the new memory level hint facility is
+ // better for what we need to do these days.
+ if (app.curRawAdj > ProcessList.FOREGROUND_APP_ADJ
+ && app.setRawAdj <= ProcessList.FOREGROUND_APP_ADJ) {
+ // If this app is transitioning from foreground to
+ // non-foreground, have it do a gc.
+ scheduleAppGcLocked(app);
+ } else if (app.curRawAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
+ && app.setRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
+ // Likewise do a gc when an app is moving in to the
+ // background (such as a service stopping).
+ scheduleAppGcLocked(app);
+ }
}
if (wasKeeping && !app.keeping) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 4ad0f45..a0aedf9 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1151,6 +1151,7 @@
try {
mService.mWindowManager.setAppVisibility(r, true);
r.sleeping = false;
+ r.app.pendingUiClean = true;
r.app.thread.scheduleWindowVisibility(r, true);
r.stopFreezingScreenLocked(false);
} catch (Exception e) {
@@ -1497,6 +1498,7 @@
next.sleeping = false;
showAskCompatModeDialogLocked(next);
+ next.app.pendingUiClean = true;
next.app.thread.scheduleResumeActivity(next,
mService.isNextTransitionForward());
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 24d92cf..9392bb4 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -99,6 +99,7 @@
String adjType; // Debugging: primary thing impacting oom_adj.
int adjTypeCode; // Debugging: adj code to report to app.
Object adjSource; // Debugging: option dependent object.
+ int adjSourceOom; // Debugging: oom_adj of adjSource's process.
Object adjTarget; // Debugging: target component impacting oom_adj.
// contains HistoryRecord objects
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 8af90ff..9067fae 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -100,6 +100,7 @@
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import android.text.format.Time;
+import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.Slog;
import android.util.SparseArray;
@@ -298,18 +299,9 @@
try {
mActivityManager.registerProcessObserver(mProcessObserver);
- } catch (RemoteException e) {
- // ouch, no foregroundActivities updates means some processes may
- // never get network access.
- Slog.e(TAG, "unable to register IProcessObserver", e);
- }
-
- try {
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
- // ouch, no alert updates means we fall back to
- // ACTION_NETWORK_STATS_UPDATED broadcasts.
- Slog.e(TAG, "unable to register INetworkManagementEventObserver", e);
+ // ignored; both services live in system_server
}
// TODO: traverse existing processes to know foreground state, or have
@@ -462,7 +454,7 @@
// caused alert to trigger.
mNetworkStats.forceUpdate();
} catch (RemoteException e) {
- Slog.w(TAG, "problem updating network stats");
+ // ignored; service lives in system_server
}
updateNetworkEnabledLocked();
@@ -495,9 +487,7 @@
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
-
final long totalBytes = getTotalBytes(policy.template, start, end);
- if (totalBytes == UNKNOWN_BYTES) continue;
if (policy.limitBytes != LIMIT_DISABLED && totalBytes >= policy.limitBytes) {
if (policy.lastSnooze >= start) {
@@ -671,7 +661,7 @@
packageName, tag, 0x0, builder.getNotification(), idReceived);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
- Slog.w(TAG, "problem during enqueueNotification: " + e);
+ // ignored; service lives in system_server
}
}
@@ -705,7 +695,7 @@
0x0, builder.getNotification(), idReceived);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
- Slog.w(TAG, "problem during enqueueNotification: " + e);
+ // ignored; service lives in system_server
}
}
@@ -716,7 +706,7 @@
mNotifManager.cancelNotificationWithTag(
packageName, tag, 0x0);
} catch (RemoteException e) {
- Slog.w(TAG, "problem during enqueueNotification: " + e);
+ // ignored; service lives in system_server
}
}
@@ -758,9 +748,7 @@
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
-
final long totalBytes = getTotalBytes(policy.template, start, end);
- if (totalBytes == UNKNOWN_BYTES) continue;
// disable data connection when over limit and not snoozed
final boolean overLimit = policy.limitBytes != LIMIT_DISABLED
@@ -810,7 +798,7 @@
try {
states = mConnManager.getAllNetworkState();
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading network state");
+ // ignored; service lives in system_server
return;
}
@@ -857,9 +845,7 @@
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
-
final long totalBytes = getTotalBytes(policy.template, start, end);
- if (totalBytes == UNKNOWN_BYTES) continue;
if (LOGD) {
Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
@@ -1006,9 +992,9 @@
// missing policy is okay, probably first boot
upgradeLegacyBackgroundData();
} catch (IOException e) {
- Slog.e(TAG, "problem reading network stats", e);
+ Log.wtf(TAG, "problem reading network policy", e);
} catch (XmlPullParserException e) {
- Slog.e(TAG, "problem reading network stats", e);
+ Log.wtf(TAG, "problem reading network policy", e);
} finally {
IoUtils.closeQuietly(fis);
}
@@ -1246,12 +1232,10 @@
final long currentTime = currentTimeMillis(false);
+ // find total bytes used under policy
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
-
- // find total bytes used under policy
final long totalBytes = getTotalBytes(policy.template, start, end);
- if (totalBytes == UNKNOWN_BYTES) return null;
// report soft and hard limits under policy
final long softLimitBytes = policy.warningBytes != WARNING_DISABLED ? policy.warningBytes
@@ -1369,6 +1353,7 @@
try {
mScreenOn = mPowerManager.isScreenOn();
} catch (RemoteException e) {
+ // ignored; service lives in system_server
}
updateRulesForScreenLocked();
}
@@ -1448,7 +1433,7 @@
// adjust stats accounting based on foreground status
mNetworkStats.setUidForeground(uid, uidForeground);
} catch (RemoteException e) {
- Slog.w(TAG, "problem dispatching foreground change");
+ // ignored; service lives in system_server
}
}
@@ -1498,9 +1483,9 @@
try {
mNetworkManager.setInterfaceQuota(iface, quotaBytes);
} catch (IllegalStateException e) {
- Slog.e(TAG, "problem setting interface quota", e);
+ Log.wtf(TAG, "problem setting interface quota", e);
} catch (RemoteException e) {
- Slog.e(TAG, "problem setting interface quota", e);
+ // ignored; service lives in system_server
}
}
@@ -1508,29 +1493,9 @@
try {
mNetworkManager.removeInterfaceQuota(iface);
} catch (IllegalStateException e) {
- Slog.e(TAG, "problem removing interface quota", e);
+ Log.wtf(TAG, "problem removing interface quota", e);
} catch (RemoteException e) {
- Slog.e(TAG, "problem removing interface quota", e);
- }
- }
-
- private void setInterfaceAlert(String iface, long alertBytes) {
- try {
- mNetworkManager.setInterfaceAlert(iface, alertBytes);
- } catch (IllegalStateException e) {
- Slog.e(TAG, "problem setting interface alert", e);
- } catch (RemoteException e) {
- Slog.e(TAG, "problem setting interface alert", e);
- }
- }
-
- private void removeInterfaceAlert(String iface) {
- try {
- mNetworkManager.removeInterfaceAlert(iface);
- } catch (IllegalStateException e) {
- Slog.e(TAG, "problem removing interface alert", e);
- } catch (RemoteException e) {
- Slog.e(TAG, "problem removing interface alert", e);
+ // ignored; service lives in system_server
}
}
@@ -1538,9 +1503,9 @@
try {
mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
} catch (IllegalStateException e) {
- Slog.e(TAG, "problem setting uid rules", e);
+ Log.wtf(TAG, "problem setting uid rules", e);
} catch (RemoteException e) {
- Slog.e(TAG, "problem setting uid rules", e);
+ // ignored; service lives in system_server
}
}
@@ -1556,7 +1521,7 @@
try {
mConnManager.setPolicyDataEnable(networkType, enabled);
} catch (RemoteException e) {
- Slog.e(TAG, "problem setting network enabled", e);
+ // ignored; service lives in system_server
}
mActiveNetworkEnabled.put(networkType, enabled);
@@ -1569,8 +1534,6 @@
return telephony.getSubscriberId();
}
- private static final long UNKNOWN_BYTES = -1;
-
private long getTotalBytes(NetworkTemplate template, long start, long end) {
try {
final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
@@ -1578,8 +1541,8 @@
final NetworkStats.Entry entry = stats.getValues(0, null);
return entry.rxBytes + entry.txBytes;
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + template);
- return UNKNOWN_BYTES;
+ // ignored; service lives in system_server
+ return 0;
}
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 4d54fd4..af29d85 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -79,6 +79,7 @@
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.EventLog;
+import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -128,7 +129,16 @@
private static final int VERSION_UID_WITH_SET = 4;
private static final int MSG_PERFORM_POLL = 0x1;
- private static final int MSG_PERFORM_POLL_DETAILED = 0x2;
+
+ /** Flags to control detail level of poll event. */
+ private static final int FLAG_POLL_NETWORK = 0x1;
+ private static final int FLAG_POLL_UID = 0x2;
+ private static final int FLAG_PERSIST_NETWORK = 0x10;
+ private static final int FLAG_PERSIST_UID = 0x20;
+ private static final int FLAG_FORCE_PERSIST = 0x100;
+
+ private static final int FLAG_POLL_ALL = FLAG_POLL_NETWORK | FLAG_POLL_UID;
+ private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
private final Context mContext;
private final INetworkManagementService mNetworkManager;
@@ -261,9 +271,7 @@
try {
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
- // ouch, no push updates means we fall back to
- // ACTION_NETWORK_STATS_POLL intervals.
- Slog.e(TAG, "unable to register INetworkManagementEventObserver", e);
+ // ignored; service lives in system_server
}
registerPollAlarmLocked();
@@ -305,7 +313,7 @@
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
mSettings.getPollInterval(), mPollIntent);
} catch (RemoteException e) {
- Slog.w(TAG, "problem registering for poll alarm: " + e);
+ // ignored; service lives in system_server
}
}
@@ -321,7 +329,7 @@
} catch (IllegalStateException e) {
Slog.w(TAG, "problem registering for global alert: " + e);
} catch (RemoteException e) {
- Slog.w(TAG, "problem registering for global alert: " + e);
+ // ignored; service lives in system_server
}
}
@@ -509,7 +517,7 @@
@Override
public void forceUpdate() {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- performPoll(true, false);
+ performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
}
/**
@@ -538,7 +546,7 @@
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified UPDATE_DEVICE_STATS
// permission above.
- performPoll(true, false);
+ performPoll(FLAG_POLL_ALL | FLAG_PERSIST_ALL);
// verify that we're watching global alert
registerGlobalAlert();
@@ -585,7 +593,8 @@
if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
// kick off background poll to collect network stats; UID stats
// are handled during normal polling interval.
- mHandler.obtainMessage(MSG_PERFORM_POLL).sendToTarget();
+ final int flags = FLAG_POLL_NETWORK | FLAG_PERSIST_NETWORK;
+ mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
// re-arm global alert for next update
registerGlobalAlert();
@@ -605,13 +614,17 @@
// take one last stats snapshot before updating iface mapping. this
// isn't perfect, since the kernel may already be counting traffic from
// the updated network.
- performPollLocked(false, false);
+
+ // poll both network and UID stats, but only persist network stats,
+ // since this codepath should stay fast. UID stats will be persisted
+ // during next alarm poll event.
+ performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_NETWORK);
final NetworkState[] states;
try {
states = mConnManager.getAllNetworkState();
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading network state");
+ // ignored; service lives in system_server
return;
}
@@ -646,15 +659,15 @@
} catch (IllegalStateException e) {
Slog.w(TAG, "problem reading network stats: " + e);
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading network stats: " + e);
+ // ignored; service lives in system_server
}
}
- private void performPoll(boolean detailedPoll, boolean forcePersist) {
+ private void performPoll(int flags) {
synchronized (mStatsLock) {
mWakeLock.acquire();
try {
- performPollLocked(detailedPoll, forcePersist);
+ performPollLocked(flags);
} finally {
mWakeLock.release();
}
@@ -664,14 +677,17 @@
/**
* Periodic poll operation, reading current statistics and recording into
* {@link NetworkStatsHistory}.
- *
- * @param detailedPoll Indicate if detailed UID stats should be collected
- * during this poll operation.
*/
- private void performPollLocked(boolean detailedPoll, boolean forcePersist) {
- if (LOGV) Slog.v(TAG, "performPollLocked()");
+ private void performPollLocked(int flags) {
+ if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
final long startRealtime = SystemClock.elapsedRealtime();
+ final boolean pollNetwork = (flags & FLAG_POLL_NETWORK) != 0;
+ final boolean pollUid = (flags & FLAG_POLL_UID) != 0;
+ final boolean persistNetwork = (flags & FLAG_PERSIST_NETWORK) != 0;
+ final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0;
+ final boolean forcePersist = (flags & FLAG_FORCE_PERSIST) != 0;
+
// try refreshing time source when stale
if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
mTime.forceRefresh();
@@ -680,41 +696,40 @@
// TODO: consider marking "untrusted" times in historical stats
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- final long persistThreshold = mSettings.getPersistThreshold();
+ final long threshold = mSettings.getPersistThreshold();
- final NetworkStats networkSnapshot;
- final NetworkStats uidSnapshot;
try {
- networkSnapshot = mNetworkManager.getNetworkStatsSummary();
- uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsUidDetail(UID_ALL) : null;
- } catch (IllegalStateException e) {
- Slog.w(TAG, "problem reading network stats: " + e);
- return;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading network stats: " + e);
- return;
- }
+ if (pollNetwork) {
+ final NetworkStats networkSnapshot = mNetworkManager.getNetworkStatsSummary();
+ performNetworkPollLocked(networkSnapshot, currentTime);
- performNetworkPollLocked(networkSnapshot, currentTime);
-
- // persist when enough network data has occurred
- final NetworkStats persistNetworkDelta = computeStatsDelta(
- mLastPersistNetworkSnapshot, networkSnapshot, true);
- if (forcePersist || persistNetworkDelta.getTotalBytes() > persistThreshold) {
- writeNetworkStatsLocked();
- mLastPersistNetworkSnapshot = networkSnapshot;
- }
-
- if (detailedPoll) {
- performUidPollLocked(uidSnapshot, currentTime);
-
- // persist when enough network data has occurred
- final NetworkStats persistUidDelta = computeStatsDelta(
- mLastPersistUidSnapshot, uidSnapshot, true);
- if (forcePersist || persistUidDelta.getTotalBytes() > persistThreshold) {
- writeUidStatsLocked();
- mLastPersistUidSnapshot = networkSnapshot;
+ // persist when enough network data has occurred
+ final NetworkStats persistNetworkDelta = computeStatsDelta(
+ mLastPersistNetworkSnapshot, networkSnapshot, true);
+ final boolean pastThreshold = persistNetworkDelta.getTotalBytes() > threshold;
+ if (forcePersist || (persistNetwork && pastThreshold)) {
+ writeNetworkStatsLocked();
+ mLastPersistNetworkSnapshot = networkSnapshot;
+ }
}
+
+ if (pollUid) {
+ final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+ performUidPollLocked(uidSnapshot, currentTime);
+
+ // persist when enough network data has occurred
+ final NetworkStats persistUidDelta = computeStatsDelta(
+ mLastPersistUidSnapshot, uidSnapshot, true);
+ final boolean pastThreshold = persistUidDelta.getTotalBytes() > threshold;
+ if (forcePersist || (persistUid && pastThreshold)) {
+ writeUidStatsLocked();
+ mLastPersistUidSnapshot = uidSnapshot;
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.wtf(TAG, "problem reading network stats", e);
+ } catch (RemoteException e) {
+ // ignored; service lives in system_server
}
if (LOGV) {
@@ -722,8 +737,8 @@
Slog.v(TAG, "performPollLocked() took " + duration + "ms");
}
- // sample stats after detailed poll
- if (detailedPoll) {
+ // sample stats after each full poll
+ if (pollNetwork && pollUid) {
performSample();
}
@@ -785,6 +800,10 @@
entry = delta.getValues(i, entry);
final NetworkIdentitySet ident = mActiveIfaces.get(entry.iface);
if (ident == null) {
+ if (entry.rxBytes > 0 || entry.rxPackets > 0 || entry.txBytes > 0
+ || entry.txPackets > 0) {
+ Log.w(TAG, "dropping UID delta from unknown iface: " + entry);
+ }
continue;
}
@@ -959,7 +978,7 @@
} catch (FileNotFoundException e) {
// missing stats is okay, probably first boot
} catch (IOException e) {
- Slog.e(TAG, "problem reading network stats", e);
+ Log.wtf(TAG, "problem reading network stats", e);
} finally {
IoUtils.closeQuietly(in);
}
@@ -1032,7 +1051,7 @@
} catch (FileNotFoundException e) {
// missing stats is okay, probably first boot
} catch (IOException e) {
- Slog.e(TAG, "problem reading uid stats", e);
+ Log.wtf(TAG, "problem reading uid stats", e);
} finally {
IoUtils.closeQuietly(in);
}
@@ -1061,7 +1080,7 @@
out.flush();
mNetworkFile.finishWrite(fos);
} catch (IOException e) {
- Slog.w(TAG, "problem writing stats: ", e);
+ Log.wtf(TAG, "problem writing stats", e);
if (fos != null) {
mNetworkFile.failWrite(fos);
}
@@ -1115,7 +1134,7 @@
out.flush();
mUidFile.finishWrite(fos);
} catch (IOException e) {
- Slog.w(TAG, "problem writing stats: ", e);
+ Log.wtf(TAG, "problem writing stats", e);
if (fos != null) {
mUidFile.failWrite(fos);
}
@@ -1142,7 +1161,7 @@
}
if (argSet.contains("poll")) {
- performPollLocked(true, true);
+ performPollLocked(FLAG_POLL_ALL | FLAG_PERSIST_ALL | FLAG_FORCE_PERSIST);
pw.println("Forced poll");
return;
}
@@ -1273,11 +1292,8 @@
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_PERFORM_POLL: {
- performPoll(false, false);
- return true;
- }
- case MSG_PERFORM_POLL_DETAILED: {
- performPoll(true, false);
+ final int flags = msg.arg1;
+ performPoll(flags);
return true;
}
default: {
@@ -1349,7 +1365,7 @@
return getSecureLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
}
public long getPersistThreshold() {
- return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 512 * KB_IN_BYTES);
+ return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 2 * MB_IN_BYTES);
}
public long getNetworkBucketDuration() {
return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 755a268..211c4da 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -5824,27 +5824,6 @@
config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dm, dw, dh);
- // We need to determine the smallest width that will occur under normal
- // operation. To this, start with the base screen size and compute the
- // width under the different possible rotations. We need to un-rotate
- // the current screen dimensions before doing this.
- int unrotDw, unrotDh;
- if (rotated) {
- unrotDw = dh;
- unrotDh = dw;
- } else {
- unrotDw = dw;
- unrotDh = dh;
- }
- config.smallestScreenWidthDp = reduceConfigWidthSize(unrotDw,
- Surface.ROTATION_0, dm.density, unrotDw);
- config.smallestScreenWidthDp = reduceConfigWidthSize(config.smallestScreenWidthDp,
- Surface.ROTATION_90, dm.density, unrotDh);
- config.smallestScreenWidthDp = reduceConfigWidthSize(config.smallestScreenWidthDp,
- Surface.ROTATION_180, dm.density, unrotDw);
- config.smallestScreenWidthDp = reduceConfigWidthSize(config.smallestScreenWidthDp,
- Surface.ROTATION_270, dm.density, unrotDh);
-
// Compute the screen layout size class.
int screenLayout;
int longSize = dw;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 54f3bb0..e7f1d9a 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -178,6 +178,7 @@
expectDefaultSettings();
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -230,6 +231,7 @@
expectDefaultSettings();
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -320,6 +322,7 @@
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -370,6 +373,7 @@
expectDefaultSettings();
expectNetworkState(buildMobile3gState(IMSI_1));
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -450,6 +454,7 @@
expectDefaultSettings();
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -507,6 +512,7 @@
expectDefaultSettings();
expectNetworkState(buildMobile3gState(IMSI_1));
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -573,6 +579,7 @@
expectDefaultSettings();
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
@@ -635,6 +642,7 @@
expectDefaultSettings();
expectNetworkState(buildWifiState());
expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE));
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index aa7568b..f769157 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -113,10 +113,15 @@
TelephonyManager telephony = TelephonyManager.getDefault();
LinkProperties linkProperties = null;
LinkCapabilities linkCapabilities = null;
+ boolean roaming = false;
+
if (state == Phone.DataState.CONNECTED) {
linkProperties = sender.getLinkProperties(apnType);
linkCapabilities = sender.getLinkCapabilities(apnType);
}
+ ServiceState ss = sender.getServiceState();
+ if (ss != null) roaming = ss.getRoaming();
+
try {
mRegistry.notifyDataConnection(
convertDataState(state),
@@ -126,7 +131,8 @@
linkProperties,
linkCapabilities,
((telephony!=null) ? telephony.getNetworkType() :
- TelephonyManager.NETWORK_TYPE_UNKNOWN));
+ TelephonyManager.NETWORK_TYPE_UNKNOWN),
+ roaming);
} catch (RemoteException ex) {
// system process is dead
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 3c83e50..1f19282 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -35,7 +35,7 @@
void notifyDataActivity(int state);
void notifyDataConnection(int state, boolean isDataConnectivityPossible,
String reason, String apn, String apnType, in LinkProperties linkProperties,
- in LinkCapabilities linkCapabilities, int networkType);
+ in LinkCapabilities linkCapabilities, int networkType, boolean roaming);
void notifyDataConnectionFailed(String reason, String apnType);
void notifyCellLocation(in Bundle cellLocation);
void notifyOtaspChanged(in int otaspMode);
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 6347f37..5e64148 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -106,6 +106,7 @@
static final String DATA_IFACE_NAME_KEY = "iface";
static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
+ static final String DATA_NETWORK_ROAMING_KEY = "networkRoaming";
static final String PHONE_IN_ECM_STATE = "phoneinECMState";
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 1a077d00e..a728d0a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -582,7 +582,12 @@
*/
@Override
protected void onRoamingOff() {
- trySetupData(Phone.REASON_ROAMING_OFF);
+ if (getDataOnRoamingEnabled() == false) {
+ notifyDataAvailability(Phone.REASON_ROAMING_OFF);
+ trySetupData(Phone.REASON_ROAMING_OFF);
+ } else {
+ notifyDataConnection(Phone.REASON_ROAMING_OFF);
+ }
}
/**
@@ -592,9 +597,11 @@
protected void onRoamingOn() {
if (getDataOnRoamingEnabled()) {
trySetupData(Phone.REASON_ROAMING_ON);
+ notifyDataConnection(Phone.REASON_ROAMING_ON);
} else {
if (DBG) log("Tear down data connection on roaming.");
cleanUpAllConnections(null);
+ notifyDataAvailability(Phone.REASON_ROAMING_ON);
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 00fb0e0..4e43fcd 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1681,23 +1681,25 @@
@Override
protected void onRoamingOff() {
if (DBG) log("onRoamingOff");
- // Notify data availability so APN can be enabled.
- notifyDataAvailability(Phone.REASON_ROAMING_OFF);
- setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
+ if (getDataOnRoamingEnabled() == false) {
+ notifyDataAvailability(Phone.REASON_ROAMING_OFF);
+ setupDataOnReadyApns(Phone.REASON_ROAMING_OFF);
+ } else {
+ notifyDataConnection(Phone.REASON_ROAMING_OFF);
+ }
}
@Override
protected void onRoamingOn() {
- // Notify data availability so APN can be enabled.
- notifyDataAvailability(Phone.REASON_ROAMING_ON);
-
if (getDataOnRoamingEnabled()) {
if (DBG) log("onRoamingOn: setup data on roaming");
setupDataOnReadyApns(Phone.REASON_ROAMING_ON);
+ notifyDataConnection(Phone.REASON_ROAMING_ON);
} else {
if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
+ notifyDataAvailability(Phone.REASON_ROAMING_ON);
}
}
diff --git a/test-runner/src/android/test/InstrumentationCoreTestRunner.java b/test-runner/src/android/test/InstrumentationCoreTestRunner.java
index ff99a74..036a2275 100644
--- a/test-runner/src/android/test/InstrumentationCoreTestRunner.java
+++ b/test-runner/src/android/test/InstrumentationCoreTestRunner.java
@@ -51,35 +51,33 @@
/**
* Convenience definition of our log tag.
- */
+ */
private static final String TAG = "InstrumentationCoreTestRunner";
-
+
/**
* True if (and only if) we are running in single-test mode (as opposed to
* batch mode).
*/
private boolean singleTest = false;
-
+
@Override
public void onCreate(Bundle arguments) {
// We might want to move this to /sdcard, if is is mounted/writable.
File cacheDir = getTargetContext().getCacheDir();
- // Set some properties that the core tests absolutely need.
+ // Set some properties that the core tests absolutely need.
System.setProperty("user.language", "en");
System.setProperty("user.region", "US");
-
+
System.setProperty("java.home", cacheDir.getAbsolutePath());
System.setProperty("user.home", cacheDir.getAbsolutePath());
System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
- System.setProperty("javax.net.ssl.trustStore",
- "/etc/security/cacerts.bks");
-
+
if (arguments != null) {
String classArg = arguments.getString(ARGUMENT_TEST_CLASS);
- singleTest = classArg != null && classArg.contains("#");
+ singleTest = classArg != null && classArg.contains("#");
}
-
+
super.onCreate(arguments);
}
@@ -89,36 +87,36 @@
runner.addTestListener(new TestListener() {
/**
- * The last test class we executed code from.
+ * The last test class we executed code from.
*/
private Class<?> lastClass;
-
+
/**
* The minimum time we expect a test to take.
*/
private static final int MINIMUM_TIME = 100;
-
+
/**
* The start time of our current test in System.currentTimeMillis().
*/
private long startTime;
-
+
public void startTest(Test test) {
if (test.getClass() != lastClass) {
lastClass = test.getClass();
printMemory(test.getClass());
}
-
+
Thread.currentThread().setContextClassLoader(
test.getClass().getClassLoader());
-
+
startTime = System.currentTimeMillis();
}
-
+
public void endTest(Test test) {
if (test instanceof TestCase) {
cleanup((TestCase)test);
-
+
/*
* Make sure all tests take at least MINIMUM_TIME to
* complete. If they don't, we wait a bit. The Cupcake
@@ -126,7 +124,7 @@
* short time, which causes headache for the CTS.
*/
long timeTaken = System.currentTimeMillis() - startTime;
-
+
if (timeTaken < MINIMUM_TIME) {
try {
Thread.sleep(MINIMUM_TIME - timeTaken);
@@ -136,15 +134,15 @@
}
}
}
-
+
public void addError(Test test, Throwable t) {
// This space intentionally left blank.
}
-
+
public void addFailure(Test test, AssertionFailedError t) {
// This space intentionally left blank.
}
-
+
/**
* Dumps some memory info.
*/
@@ -154,7 +152,7 @@
long total = runtime.totalMemory();
long free = runtime.freeMemory();
long used = total - free;
-
+
Log.d(TAG, "Total memory : " + total);
Log.d(TAG, "Used memory : " + used);
Log.d(TAG, "Free memory : " + free);
@@ -170,7 +168,7 @@
*/
private void cleanup(TestCase test) {
Class<?> clazz = test.getClass();
-
+
while (clazz != TestCase.class) {
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
@@ -185,15 +183,15 @@
}
}
}
-
+
clazz = clazz.getSuperclass();
}
}
-
+
});
-
+
return runner;
- }
+ }
@Override
List<Predicate<TestMethod>> getBuilderRequirements() {
diff --git a/tests/DataIdleTest/Android.mk b/tests/DataIdleTest/Android.mk
new file mode 100644
index 0000000..acb46c5
--- /dev/null
+++ b/tests/DataIdleTest/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2011 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.
+#
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := DataIdleTest
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# We need to sign it to get access to the network usage history.
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/tests/DataIdleTest/AndroidManifest.xml b/tests/DataIdleTest/AndroidManifest.xml
new file mode 100644
index 0000000..2792eec
--- /dev/null
+++ b/tests/DataIdleTest/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.dataidle"
+ android:sharedUserId="android.uid.system">
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
+ <application >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.tests.dataidle"
+ android:label="Idle Bandwidth Tests" />
+
+</manifest>
diff --git a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
new file mode 100644
index 0000000..637f0d2
--- /dev/null
+++ b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 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 com.android.tests.dataidle;
+
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.NetworkStats.Entry;
+import android.net.NetworkTemplate;
+import android.net.NetworkStats;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.test.InstrumentationTestCase;
+import android.test.InstrumentationTestRunner;
+import android.util.Log;
+
+/**
+ * A test that dumps data usage to instrumentation out, used for measuring data usage for idle
+ * devices.
+ */
+public class DataIdleTest extends InstrumentationTestCase {
+
+ private TelephonyManager mTelephonyManager;
+ private INetworkStatsService mStatsService;
+
+ private static final String LOG_TAG = "DataIdleTest";
+ private final static int INSTRUMENTATION_IN_PROGRESS = 2;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ Context c = getInstrumentation().getTargetContext();
+ mStatsService = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ mTelephonyManager = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ /**
+ * Test that dumps all the data usage metrics for wifi to instrumentation out.
+ */
+ public void testWifiIdle() {
+ NetworkTemplate template = NetworkTemplate.buildTemplateWifi();
+ fetchStats(template);
+ }
+
+ /**
+ * Test that dumps all the data usage metrics for all mobile to instrumentation out.
+ */
+ public void testMobile() {
+ String subscriberId = mTelephonyManager.getSubscriberId();
+ NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ fetchStats(template);
+ }
+
+ /**
+ * Helper method that fetches all the network stats available and reports it
+ * to instrumentation out.
+ * @param template {link {@link NetworkTemplate} to match.
+ */
+ private void fetchStats(NetworkTemplate template) {
+ try {
+ NetworkStats stats = mStatsService.getSummaryForAllUid(template, Long.MIN_VALUE,
+ Long.MAX_VALUE, false);
+ reportStats(stats);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Failed to fetch network stats for wifi.");
+ }
+ }
+
+ /**
+ * Print network data usage stats to instrumentation out
+ * @param stats {@link NetworkorStats} to print
+ */
+ void reportStats(NetworkStats stats) {
+ for (int i = 0; i < stats.size(); ++i) {
+ Entry statsEntry = stats.getValues(i, null);
+ Bundle result = new Bundle();
+ result.putInt("uid", statsEntry.uid);
+ result.putInt("tag", statsEntry.tag);
+ result.putInt("set", statsEntry.set);
+ result.putString("iface", statsEntry.iface);
+ result.putLong("rxBytes", statsEntry.rxBytes);
+ result.putLong("txBytes", statsEntry.txBytes);
+ getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, result);
+ }
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/Android.mk b/tests/RenderScriptTests/ImageProcessing/Android.mk
index 507cc92..d7486e8 100644
--- a/tests/RenderScriptTests/ImageProcessing/Android.mk
+++ b/tests/RenderScriptTests/ImageProcessing/Android.mk
@@ -17,7 +17,9 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-renderscript-files-under, src)
diff --git a/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml b/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml
index 174cc65..2232b98 100644
--- a/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml
+++ b/tests/RenderScriptTests/ImageProcessing/AndroidManifest.xml
@@ -2,10 +2,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.rs.image">
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk android:minSdkVersion="11" />
<application android:label="Image Processing"
android:hardwareAccelerated="true">
+ <uses-library android:name="android.test.runner" />
<activity android:name="ImageProcessingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -13,4 +14,9 @@
</intent-filter>
</activity>
</application>
+
+ <instrumentation android:name=".ImageProcessingTestRunner"
+ android:targetPackage="com.android.rs.image"
+ android:label="Test runner for Image Processing Benchmark Test"
+ />
</manifest>
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 9aa70b0..3615f60 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -33,11 +33,13 @@
import android.widget.SeekBar;
import android.widget.TextView;
import android.view.View;
+import android.util.Log;
import java.lang.Math;
public class ImageProcessingActivity extends Activity
implements SurfaceHolder.Callback,
SeekBar.OnSeekBarChangeListener {
+ private final String TAG = "Img";
private Bitmap mBitmapIn;
private Bitmap mBitmapOut;
private ScriptC_threshold mScript;
@@ -268,7 +270,15 @@
// button hook
public void benchmark(View v) {
- android.util.Log.v("Img", "Benchmarking");
+ long t = getBenchmark();
+ //long javaTime = javaFilter();
+ //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
+ mBenchmarkResult.setText("Result: " + t + " ms");
+ }
+
+ // For benchmark test
+ public long getBenchmark() {
+ Log.v(TAG, "Benchmarking");
int oldRadius = mRadius;
mRadius = MAX_RADIUS;
mScript.set_radius(mRadius);
@@ -279,16 +289,12 @@
mOutPixelsAllocation.copyTo(mBitmapOut);
t = java.lang.System.currentTimeMillis() - t;
- android.util.Log.v("Img", "Renderscript frame time core ms " + t);
-
- //long javaTime = javaFilter();
- //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
- mBenchmarkResult.setText("Result: " + t + " ms");
-
+ Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t);
mRadius = oldRadius;
mScript.set_radius(mRadius);
mScript.invoke_filter();
mOutPixelsAllocation.copyTo(mBitmapOut);
+ return t;
}
}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
new file mode 100644
index 0000000..d2298da
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 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 com.android.rs.image;
+
+import android.os.Bundle;
+import android.os.Environment;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * ImageProcessing benchmark test.
+ * To run the test, please use command
+ *
+ * adb shell am instrument -w com.android.rs.image/.ImageProcessingTestRunner
+ *
+ */
+public class ImageProcessingTest extends ActivityInstrumentationTestCase2<ImageProcessingActivity> {
+ private final String TAG = "ImageProcessingTest";
+ private final String RESULT_FILE = "image_processing_result.txt";
+ private ImageProcessingActivity mAct;
+
+ public ImageProcessingTest() {
+ super(ImageProcessingActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mAct = getActivity();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * ImageProcessing benchmark test
+ */
+ @LargeTest
+ public void testImageProcessingBench() {
+ long t = mAct.getBenchmark();
+ Log.v(TAG, "t = " + t);
+
+ // write result into a file
+ File externalStorage = Environment.getExternalStorageDirectory();
+ if (!externalStorage.canWrite()) {
+ Log.v(TAG, "sdcard is not writable");
+ return;
+ }
+ File resultFile = new File(externalStorage, RESULT_FILE);
+ resultFile.setWritable(true, false);
+ try {
+ BufferedWriter results = new BufferedWriter(new FileWriter(resultFile));
+ results.write("Renderscript frame time core: " + t + " ms");
+ results.close();
+ Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
+ } catch (IOException e) {
+ Log.v(TAG, "Unable to write result file " + e.getMessage());
+ }
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java
new file mode 100644
index 0000000..4e27b7f
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTestRunner.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 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 com.android.rs.image;
+
+import com.android.rs.image.ImageProcessingTest;
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+import junit.framework.TestSuite;
+
+/**
+ * Run the ImageProcessing benchmark test
+ * adb shell am instrument -w com.android.rs.image/.ImageProcessingTestRunner
+ *
+ */
+public class ImageProcessingTestRunner extends InstrumentationTestRunner {
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(ImageProcessingTest.class);
+ return suite;
+ }
+}
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
index 84c7166..e2d820e 100644
--- a/voip/jni/rtp/AmrCodec.cpp
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -52,7 +52,7 @@
int set(int sampleRate, const char *fmtp);
int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, void *payload, int length);
+ int decode(int16_t *samples, int count, void *payload, int length);
private:
void *mEncoder;
@@ -128,7 +128,7 @@
return length;
}
-int AmrCodec::decode(int16_t *samples, void *payload, int length)
+int AmrCodec::decode(int16_t *samples, int count, void *payload, int length)
{
unsigned char *bytes = (unsigned char *)payload;
Frame_Type_3GPP type;
@@ -213,7 +213,7 @@
}
int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, void *payload, int length);
+ int decode(int16_t *samples, int count, void *payload, int length);
private:
void *mEncoder;
@@ -239,20 +239,24 @@
return -1;
}
-int GsmEfrCodec::decode(int16_t *samples, void *payload, int length)
+int GsmEfrCodec::decode(int16_t *samples, int count, void *payload, int length)
{
unsigned char *bytes = (unsigned char *)payload;
- if (length == 31 && (bytes[0] >> 4) == 0x0C) {
+ int n = 0;
+ while (n + 160 <= count && length >= 31 && (bytes[0] >> 4) == 0x0C) {
for (int i = 0; i < 30; ++i) {
bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
}
bytes[30] <<= 4;
- if (AMRDecode(mDecoder, AMR_122, bytes, samples, MIME_IETF) == 31) {
- return 160;
+ if (AMRDecode(mDecoder, AMR_122, bytes, &samples[n], MIME_IETF) != 31) {
+ break;
}
+ n += 160;
+ length -= 31;
+ bytes += 31;
}
- return -1;
+ return n;
}
} // namespace
diff --git a/voip/jni/rtp/AudioCodec.h b/voip/jni/rtp/AudioCodec.h
index e389255..741730b 100644
--- a/voip/jni/rtp/AudioCodec.h
+++ b/voip/jni/rtp/AudioCodec.h
@@ -30,7 +30,7 @@
// Returns the length of payload in bytes.
virtual int encode(void *payload, int16_t *samples) = 0;
// Returns the number of decoded samples.
- virtual int decode(int16_t *samples, void *payload, int length) = 0;
+ virtual int decode(int16_t *samples, int count, void *payload, int length) = 0;
};
AudioCodec *newAudioCodec(const char *codecName);
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index 5f07bb5..93c809e 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -395,7 +395,8 @@
mLatencyTimer = tick;
}
- if (mBufferTail - mBufferHead > BUFFER_SIZE - mInterval) {
+ int count = (BUFFER_SIZE - (mBufferTail - mBufferHead)) * mSampleRate;
+ if (count < mSampleCount) {
// Buffer overflow. Drop the packet.
LOGV("stream[%d] buffer overflow", mSocket);
recv(mSocket, &c, 1, MSG_DONTWAIT);
@@ -403,19 +404,18 @@
}
// Receive the packet and decode it.
- int16_t samples[mSampleCount];
- int length = 0;
+ int16_t samples[count];
if (!mCodec) {
// Special case for device stream.
- length = recv(mSocket, samples, sizeof(samples),
+ count = recv(mSocket, samples, sizeof(samples),
MSG_TRUNC | MSG_DONTWAIT) >> 1;
} else {
__attribute__((aligned(4))) uint8_t buffer[2048];
sockaddr_storage remote;
- socklen_t len = sizeof(remote);
+ socklen_t addrlen = sizeof(remote);
- length = recvfrom(mSocket, buffer, sizeof(buffer),
- MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &len);
+ int length = recvfrom(mSocket, buffer, sizeof(buffer),
+ MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &addrlen);
// Do we need to check SSRC, sequence, and timestamp? They are not
// reliable but at least they can be used to identify duplicates?
@@ -433,14 +433,15 @@
}
length -= offset;
if (length >= 0) {
- length = mCodec->decode(samples, &buffer[offset], length);
+ length = mCodec->decode(samples, count, &buffer[offset], length);
}
if (length > 0 && mFixRemote) {
mRemote = remote;
mFixRemote = false;
}
+ count = length;
}
- if (length <= 0) {
+ if (count <= 0) {
LOGV("stream[%d] decoder error", mSocket);
return;
}
@@ -462,7 +463,7 @@
// Append to the jitter buffer.
int tail = mBufferTail * mSampleRate;
- for (int i = 0; i < mSampleCount; ++i) {
+ for (int i = 0; i < count; ++i) {
mBuffer[tail & mBufferMask] = samples[i];
++tail;
}
diff --git a/voip/jni/rtp/G711Codec.cpp b/voip/jni/rtp/G711Codec.cpp
index a467acf..ef54863 100644
--- a/voip/jni/rtp/G711Codec.cpp
+++ b/voip/jni/rtp/G711Codec.cpp
@@ -39,7 +39,7 @@
return mSampleCount;
}
int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, void *payload, int length);
+ int decode(int16_t *samples, int count, void *payload, int length);
private:
int mSampleCount;
};
@@ -64,9 +64,12 @@
return mSampleCount;
}
-int UlawCodec::decode(int16_t *samples, void *payload, int length)
+int UlawCodec::decode(int16_t *samples, int count, void *payload, int length)
{
int8_t *ulaws = (int8_t *)payload;
+ if (length > count) {
+ length = count;
+ }
for (int i = 0; i < length; ++i) {
int ulaw = ~ulaws[i];
int exponent = (ulaw >> 4) & 0x07;
@@ -87,7 +90,7 @@
return mSampleCount;
}
int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, void *payload, int length);
+ int decode(int16_t *samples, int count, void *payload, int length);
private:
int mSampleCount;
};
@@ -111,9 +114,12 @@
return mSampleCount;
}
-int AlawCodec::decode(int16_t *samples, void *payload, int length)
+int AlawCodec::decode(int16_t *samples, int count, void *payload, int length)
{
int8_t *alaws = (int8_t *)payload;
+ if (length > count) {
+ length = count;
+ }
for (int i = 0; i < length; ++i) {
int alaw = alaws[i] ^ 0x55;
int exponent = (alaw >> 4) & 0x07;
diff --git a/voip/jni/rtp/GsmCodec.cpp b/voip/jni/rtp/GsmCodec.cpp
index 8d2286e..61dfdc9 100644
--- a/voip/jni/rtp/GsmCodec.cpp
+++ b/voip/jni/rtp/GsmCodec.cpp
@@ -44,7 +44,7 @@
}
int encode(void *payload, int16_t *samples);
- int decode(int16_t *samples, void *payload, int length);
+ int decode(int16_t *samples, int count, void *payload, int length);
private:
gsm mEncode;
@@ -57,13 +57,17 @@
return 33;
}
-int GsmCodec::decode(int16_t *samples, void *payload, int length)
+int GsmCodec::decode(int16_t *samples, int count, void *payload, int length)
{
- if (length == 33 &&
- gsm_decode(mDecode, (unsigned char *)payload, samples) == 0) {
- return 160;
+ unsigned char *bytes = (unsigned char *)payload;
+ int n = 0;
+ while (n + 160 <= count && length >= 33 &&
+ gsm_decode(mDecode, bytes, &samples[n]) == 0) {
+ n += 160;
+ length -= 33;
+ bytes += 33;
}
- return -1;
+ return n;
}
} // namespace
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 1a7638d7..6cc09e9 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -261,10 +261,10 @@
public static String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
if (config == null) return null;
List<String> args = new ArrayList<String>();
- Wps wpsConfig = config.wpsConfig;
+ Wps wps = config.wps;
args.add(config.deviceAddress);
- switch (wpsConfig.setup) {
+ switch (wps.setup) {
case PBC:
args.add("pbc");
break;
@@ -274,11 +274,11 @@
args.add("display");
break;
case KEYPAD:
- args.add(wpsConfig.pin);
+ args.add(wps.pin);
args.add("keypad");
break;
case LABEL:
- args.add(wpsConfig.pin);
+ args.add(wps.pin);
args.add("label");
default:
break;
diff --git a/wifi/java/android/net/wifi/Wps.java b/wifi/java/android/net/wifi/Wps.java
index 175c7b5..6d006960 100644
--- a/wifi/java/android/net/wifi/Wps.java
+++ b/wifi/java/android/net/wifi/Wps.java
@@ -27,10 +27,12 @@
/**
* A class representing Wi-Fi Protected Setup
* @hide
+ *
+ * {@see WifiP2pConfig}
*/
public class Wps implements Parcelable {
- /* Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */
+ /** Wi-Fi Protected Setup. www.wi-fi.org/wifi-protected-setup has details */
public enum Setup {
/* Push button configuration */
PBC,
@@ -49,6 +51,7 @@
/** @hide */
public String BSSID;
+ /** Passed with pin method configuration */
public String pin;
/** @hide */
@@ -60,7 +63,6 @@
/** @hide */
public LinkProperties linkProperties;
- /** @hide */
public Wps() {
setup = Setup.INVALID;
BSSID = null;
diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
index a0c7dd1..381a450 100644
--- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
+++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
@@ -26,6 +26,5 @@
interface IWifiP2pManager
{
Messenger getMessenger();
- boolean isP2pSupported();
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 3fb67f9..e359ce5 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -22,20 +22,22 @@
import android.os.Parcel;
/**
- * A class representing a Wi-Fi P2p configuration
+ * A class representing a Wi-Fi P2p configuration for setting up a connection
* @hide
+ *
+ * {@see WifiP2pManager}
*/
public class WifiP2pConfig implements Parcelable {
/**
- * Device address
+ * The device MAC address uniquely identifies a Wi-Fi p2p device
*/
public String deviceAddress;
/**
- * WPS configuration
+ * Wi-Fi Protected Setup information
*/
- public Wps wpsConfig;
+ public Wps wps;
/**
* This is an integer value between 0 and 15 where 0 indicates the least
@@ -61,11 +63,11 @@
public WifiP2pConfig() {
//set defaults
- wpsConfig = new Wps();
- wpsConfig.setup = Setup.PBC;
+ wps = new Wps();
+ wps.setup = Setup.PBC;
}
- /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
+ /** P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 {@hide}*/
public WifiP2pConfig(String supplicantEvent) throws IllegalArgumentException {
String[] tokens = supplicantEvent.split(" ");
@@ -74,7 +76,7 @@
}
deviceAddress = tokens[1];
- wpsConfig = new Wps();
+ wps = new Wps();
if (tokens.length > 2) {
String[] nameVal = tokens[2].split("=");
@@ -87,28 +89,29 @@
//As defined in wps/wps_defs.h
switch (devPasswdId) {
case 0x00:
- wpsConfig.setup = Setup.LABEL;
+ wps.setup = Setup.LABEL;
break;
case 0x01:
- wpsConfig.setup = Setup.KEYPAD;
+ wps.setup = Setup.KEYPAD;
break;
case 0x04:
- wpsConfig.setup = Setup.PBC;
+ wps.setup = Setup.PBC;
break;
case 0x05:
- wpsConfig.setup = Setup.DISPLAY;
+ wps.setup = Setup.DISPLAY;
break;
default:
- wpsConfig.setup = Setup.PBC;
+ wps.setup = Setup.PBC;
break;
}
}
}
+ /** @hide */
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("\n address: ").append(deviceAddress);
- sbuf.append("\n wps: ").append(wpsConfig);
+ sbuf.append("\n wps: ").append(wps);
sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
sbuf.append("\n persist: ").append(persist.toString());
return sbuf.toString();
@@ -129,7 +132,7 @@
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(deviceAddress);
- dest.writeParcelable(wpsConfig, flags);
+ dest.writeParcelable(wps, flags);
dest.writeInt(groupOwnerIntent);
dest.writeString(persist.name());
}
@@ -140,7 +143,7 @@
public WifiP2pConfig createFromParcel(Parcel in) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = in.readString();
- config.wpsConfig = (Wps) in.readParcelable(null);
+ config.wps = (Wps) in.readParcelable(null);
config.groupOwnerIntent = in.readInt();
config.persist = Persist.valueOf(in.readString());
return config;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 7908726..99c585f 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -25,17 +25,20 @@
/**
* A class representing a Wi-Fi p2p device
* @hide
+ *
+ * {@see WifiP2pManager}
*/
public class WifiP2pDevice implements Parcelable {
private static final String TAG = "WifiP2pDevice";
+
/**
- * Device name
+ * The device name is a user friendly string to identify a Wi-Fi p2p device
*/
public String deviceName;
/**
- * Device MAC address
+ * The device MAC address uniquely identifies a Wi-Fi p2p device
*/
public String deviceAddress;
@@ -46,35 +49,30 @@
* P2P Interface Address and the group interface will be created with
* address as the local address in case of successfully completed
* negotiation.
+ * @hide
*/
public String interfaceAddress;
/**
- * Primary device type
+ * Primary device type identifies the type of device. For example, an application
+ * could filter the devices discovered to only display printers if the purpose is to
+ * enable a printing action from the user. See the Wi-Fi Direct technical specification
+ * for the full list of standard device types supported.
*/
public String primaryDeviceType;
/**
- * Secondary device type
+ * Secondary device type is an optional attribute that can be provided by a device in
+ * addition to the primary device type.
*/
public String secondaryDeviceType;
// These definitions match the ones in wpa_supplicant
/* WPS config methods supported */
- private static final int WPS_CONFIG_USBA = 0x0001;
- private static final int WPS_CONFIG_ETHERNET = 0x0002;
- private static final int WPS_CONFIG_LABEL = 0x0004;
private static final int WPS_CONFIG_DISPLAY = 0x0008;
- private static final int WPS_CONFIG_EXT_NFC_TOKEN = 0x0010;
- private static final int WPS_CONFIG_INT_NFC_TOKEN = 0x0020;
- private static final int WPS_CONFIG_NFC_INTERFACE = 0x0040;
private static final int WPS_CONFIG_PUSHBUTTON = 0x0080;
private static final int WPS_CONFIG_KEYPAD = 0x0100;
- private static final int WPS_CONFIG_VIRT_PUSHBUTTON = 0x0280;
- private static final int WPS_CONFIG_PHY_PUSHBUTTON = 0x0480;
- private static final int WPS_CONFIG_VIRT_DISPLAY = 0x2008;
- private static final int WPS_CONFIG_PHY_DISPLAY = 0x4008;
/* Device Capability bitmap */
private static final int DEVICE_CAPAB_SERVICE_DISCOVERY = 1;
@@ -95,19 +93,23 @@
/**
* WPS config methods supported
+ * @hide
*/
public int wpsConfigMethodsSupported;
/**
* Device capability
+ * @hide
*/
public int deviceCapability;
/**
* Group capability
+ * @hide
*/
public int groupCapability;
+ /** Device connection status */
public enum Status {
CONNECTED,
INVITED,
@@ -118,7 +120,7 @@
public Status status = Status.UNAVAILABLE;
- public WifiP2pDevice() {
+ WifiP2pDevice() {
}
/**
@@ -144,6 +146,7 @@
* group_capab=0x0
*
* Note: The events formats can be looked up in the wpa_supplicant code
+ * @hide
*/
public WifiP2pDevice(String string) throws IllegalArgumentException {
String[] tokens = string.split(" ");
@@ -198,11 +201,33 @@
}
}
+ /** Returns true if WPS push button configuration is supported */
+ public boolean wpsPbcSupported() {
+ return (wpsConfigMethodsSupported & WPS_CONFIG_PUSHBUTTON) != 0;
+ }
+
+ /** Returns true if WPS keypad configuration is supported */
+ public boolean wpsKeypadSupported() {
+ return (wpsConfigMethodsSupported & WPS_CONFIG_KEYPAD) != 0;
+ }
+
+ /** Returns true if WPS display configuration is supported */
+ public boolean wpsDisplaySupported() {
+ return (wpsConfigMethodsSupported & WPS_CONFIG_DISPLAY) != 0;
+ }
+
+ /** Returns true if the device is capable of service discovery */
+ public boolean isServiceDiscoveryCapable() {
+ return (deviceCapability & DEVICE_CAPAB_SERVICE_DISCOVERY) != 0;
+ }
+
+ /** Returns true if the device is a group owner */
public boolean isGroupOwner() {
return (groupCapability & GROUP_CAPAB_GROUP_OWNER) != 0;
}
@Override
+ /** @hide */
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof WifiP2pDevice)) return false;
@@ -214,6 +239,7 @@
return other.deviceAddress.equals(deviceAddress);
}
+ /** @hide */
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("Device: ").append(deviceName);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
index aa3554c..242bce0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -28,22 +28,25 @@
/**
* A class representing a Wi-Fi P2p device list
* @hide
+ *
+ * {@see WifiP2pManager}
*/
public class WifiP2pDeviceList implements Parcelable {
private Collection<WifiP2pDevice> mDevices;
- public WifiP2pDeviceList() {
+ WifiP2pDeviceList() {
mDevices = new ArrayList<WifiP2pDevice>();
}
- //copy constructor
+ /** copy constructor {@hide} */
public WifiP2pDeviceList(WifiP2pDeviceList source) {
if (source != null) {
mDevices = source.getDeviceList();
}
}
+ /** @hide */
public WifiP2pDeviceList(ArrayList<WifiP2pDevice> devices) {
mDevices = new ArrayList<WifiP2pDevice>();
for (WifiP2pDevice device : devices) {
@@ -51,12 +54,14 @@
}
}
+ /** @hide */
public boolean clear() {
if (mDevices.isEmpty()) return false;
mDevices.clear();
return true;
}
+ /** @hide */
public void update(WifiP2pDevice device) {
if (device == null) return;
for (WifiP2pDevice d : mDevices) {
@@ -75,15 +80,18 @@
mDevices.add(device);
}
+ /** @hide */
public boolean remove(WifiP2pDevice device) {
if (device == null) return false;
return mDevices.remove(device);
}
+ /** Get the list of devices */
public Collection<WifiP2pDevice> getDeviceList() {
return Collections.unmodifiableCollection(mDevices);
}
+ /** @hide */
public String toString() {
StringBuffer sbuf = new StringBuffer();
for (WifiP2pDevice device : mDevices) {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index e35d360..48f210b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -27,6 +27,8 @@
/**
* A class representing a Wi-Fi P2p group
* @hide
+ *
+ * {@see WifiP2pManager}
*/
public class WifiP2pGroup implements Parcelable {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
index a02175e..81b7708 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
@@ -23,15 +23,20 @@
import java.net.UnknownHostException;
/**
- * A class representing connection info on Wi-fi P2p
+ * A class representing connection information about a Wi-Fi p2p group
* @hide
+ *
+ * {@see WifiP2pManager}
*/
public class WifiP2pInfo implements Parcelable {
+ /** Indicates if a p2p group has been successfully formed */
public boolean groupFormed;
+ /** Indicates if the current device is the group owner */
public boolean isGroupOwner;
+ /** Group owner address */
public InetAddress groupOwnerAddress;
/** @hide */
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 11de9c4..5715186 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -64,7 +64,7 @@
* use {@link #requestConnectionInfo} to fetch the connection details. Connection information
* can be obtained with {@link #connectionInfoInResponse} on a {@link #RESPONSE_CONNECTION_INFO}
* message. The connection info {@link WifiP2pInfo} contains the address of the group owner
- * {@link WifiP2pInfo#groupOwnerAddress} and a flag {@link #WifiP2pInfo#isGroupOwner} to indicate
+ * {@link WifiP2pInfo#groupOwnerAddress} and a flag {@link WifiP2pInfo#isGroupOwner} to indicate
* if the current device is a p2p group owner. A p2p client can thus communicate with
* the p2p group owner through a socket connection.
*
@@ -85,6 +85,7 @@
* {@see WifiP2pGroup}
* {@see WifiP2pDevice}
* {@see WifiP2pDeviceList}
+ * {@see android.net.wifi.Wps}
* @hide
*/
public class WifiP2pManager {
@@ -399,15 +400,6 @@
}
}
- /** @hide */
- public boolean isP2pSupported() {
- try {
- return mService.isP2pSupported();
- } catch (RemoteException e) {
- return false;
- }
- }
-
/**
* Sends in a request to the system to enable p2p. This will pop up a dialog
* to the user and upon authorization will enable p2p.
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 0ef2e13..e2b2249 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -155,8 +155,9 @@
mInterface = SystemProperties.get("wifi.interface", "wlan0");
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
- mP2pSupported = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_wifi_p2p_support);
+ mP2pSupported = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_DIRECT);
+
mDeviceType = mContext.getResources().getString(
com.android.internal.R.string.config_wifi_p2p_device_type);
mDeviceName = getDefaultDeviceName();
@@ -218,14 +219,6 @@
return new Messenger(mP2pStateMachine.getHandler());
}
- /**
- * Return if p2p is supported
- */
- public boolean isP2pSupported() {
- enforceAccessPermission();
- return mP2pSupported;
- }
-
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1034,7 +1027,7 @@
private void notifyP2pGoNegotationRequest(WifiP2pConfig config) {
Resources r = Resources.getSystem();
- Wps wpsConfig = config.wpsConfig;
+ Wps wps = config.wps;
final View textEntryView = LayoutInflater.from(mContext)
.inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
@@ -1047,10 +1040,10 @@
if (DBG) logd(getName() + " connect " + pin.getText());
if (pin.getVisibility() == View.GONE) {
- mSavedGoNegotiationConfig.wpsConfig.setup = Setup.PBC;
+ mSavedGoNegotiationConfig.wps.setup = Setup.PBC;
} else {
- mSavedGoNegotiationConfig.wpsConfig.setup = Setup.KEYPAD;
- mSavedGoNegotiationConfig.wpsConfig.pin = pin.getText().toString();
+ mSavedGoNegotiationConfig.wps.setup = Setup.KEYPAD;
+ mSavedGoNegotiationConfig.wps.pin = pin.getText().toString();
}
sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
mSavedGoNegotiationConfig = null;
@@ -1065,7 +1058,7 @@
})
.create();
- if (wpsConfig.setup == Setup.PBC) {
+ if (wps.setup == Setup.PBC) {
pin.setVisibility(View.GONE);
dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
config.deviceAddress));
@@ -1218,6 +1211,11 @@
mReplyChannel.replyToMessage(msg, what);
}
+ private void replyToMessage(Message msg, int what, int arg1) {
+ if (msg.replyTo == null) return;
+ mReplyChannel.replyToMessage(msg, what, arg1);
+ }
+
private void replyToMessage(Message msg, int what, Object obj) {
if (msg.replyTo == null) return;
mReplyChannel.replyToMessage(msg, what, obj);