Merge "Addressing issues associated RemoteViewsAdapter's service crashes / disconnects"
diff --git a/api/current.xml b/api/current.xml
index 6ff0ee3..f915fad 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3265,6 +3265,17 @@
visibility="public"
>
</field>
+<field name="customTokens"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843593"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="cycles"
type="int"
transient="false"
@@ -18308,6 +18319,28 @@
visibility="public"
>
</field>
+<field name="KEY_CALLER_PID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""callerPid""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="KEY_CALLER_UID"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""callerUid""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="KEY_ERROR_CODE"
type="java.lang.String"
transient="false"
@@ -18555,6 +18588,28 @@
</parameter>
<parameter name="prefId" type="int">
</parameter>
+<parameter name="customTokens" type="boolean">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorDescription"
+ type="android.accounts.AuthenticatorDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="labelId" type="int">
+</parameter>
+<parameter name="iconId" type="int">
+</parameter>
+<parameter name="smallIconId" type="int">
+</parameter>
+<parameter name="prefId" type="int">
+</parameter>
</constructor>
<method name="describeContents"
return="int"
@@ -18615,6 +18670,16 @@
visibility="public"
>
</field>
+<field name="customTokens"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="iconId"
type="int"
transient="false"
@@ -32206,6 +32271,19 @@
visibility="public"
>
</constructor>
+<method name="destroyLoader"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
<method name="dump"
return="void"
abstract="true"
@@ -32274,12 +32352,12 @@
</method>
<method name="stopLoader"
return="void"
- abstract="true"
+ abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="id" type="int">
@@ -32323,123 +32401,20 @@
<parameter name="data" type="D">
</parameter>
</method>
+<method name="onLoaderReset"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="loader" type="android.content.Loader<D>">
+</parameter>
+</method>
</interface>
-<class name="LoaderManagingFragment"
- extends="android.app.Fragment"
- abstract="true"
- static="false"
- final="false"
- deprecated="deprecated"
- visibility="public"
->
-<implements name="android.content.Loader.OnLoadCompleteListener">
-</implements>
-<constructor name="LoaderManagingFragment"
- type="android.app.LoaderManagingFragment"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</constructor>
-<method name="getLoader"
- return="android.content.Loader<D>"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-<method name="onCreateLoader"
- return="android.content.Loader<D>"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="id" type="int">
-</parameter>
-<parameter name="args" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="onInitializeLoaders"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-</method>
-<method name="onLoadComplete"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="loader" type="android.content.Loader<D>">
-</parameter>
-<parameter name="data" type="D">
-</parameter>
-</method>
-<method name="onLoadFinished"
- return="void"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="loader" type="android.content.Loader<D>">
-</parameter>
-<parameter name="data" type="D">
-</parameter>
-</method>
-<method name="startLoading"
- return="android.content.Loader<D>"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-<parameter name="args" type="android.os.Bundle">
-</parameter>
-</method>
-<method name="stopLoading"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="id" type="int">
-</parameter>
-</method>
-</class>
<class name="LocalActivityManager"
extends="java.lang.Object"
abstract="false"
@@ -49433,17 +49408,6 @@
<parameter name="cursor" type="android.database.Cursor">
</parameter>
</method>
-<method name="destroy"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</method>
<method name="getProjection"
return="java.lang.String[]"
abstract="false"
@@ -54750,12 +54714,12 @@
</method>
<method name="destroy"
return="void"
- abstract="true"
+ abstract="false"
native="false"
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -54818,6 +54782,17 @@
<parameter name="listener" type="android.content.Loader.OnLoadCompleteListener<D>">
</parameter>
</method>
+<method name="reset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="startLoading"
return="void"
abstract="true"
@@ -223321,7 +223296,7 @@
abstract="false"
static="false"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="CacheManager"
@@ -223339,7 +223314,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -223361,7 +223336,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="url" type="java.lang.String">
@@ -223376,7 +223351,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -223387,7 +223362,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="url" type="java.lang.String">
@@ -223412,7 +223387,7 @@
abstract="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="CacheManager.CacheResult"
@@ -234083,7 +234058,7 @@
type="android.widget.CursorAdapter"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -234256,7 +234231,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="protected"
>
<parameter name="context" type="android.content.Context">
@@ -234266,23 +234241,6 @@
<parameter name="autoRequery" type="boolean">
</parameter>
</method>
-<method name="init"
- return="void"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="context" type="android.content.Context">
-</parameter>
-<parameter name="c" type="android.database.Cursor">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-</method>
<method name="newDropDownView"
return="android.view.View"
abstract="false"
@@ -234354,6 +234312,19 @@
<parameter name="filterQueryProvider" type="android.widget.FilterQueryProvider">
</parameter>
</method>
+<method name="swapCursor"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newCursor" type="android.database.Cursor">
+</parameter>
+</method>
<field name="FLAG_AUTO_REQUERY"
type="int"
transient="false"
@@ -234361,7 +234332,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -243872,7 +243843,7 @@
type="android.widget.ResourceCursorAdapter"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -245304,6 +245275,24 @@
type="android.widget.SimpleCursorAdapter"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="layout" type="int">
+</parameter>
+<parameter name="c" type="android.database.Cursor">
+</parameter>
+<parameter name="from" type="java.lang.String[]">
+</parameter>
+<parameter name="to" type="int[]">
+</parameter>
+</constructor>
+<constructor name="SimpleCursorAdapter"
+ type="android.widget.SimpleCursorAdapter"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -245317,6 +245306,8 @@
</parameter>
<parameter name="to" type="int[]">
</parameter>
+<parameter name="flags" type="int">
+</parameter>
</constructor>
<method name="bindView"
return="void"
@@ -251263,7 +251254,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
@@ -335167,7 +335158,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -335235,7 +335226,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -335259,7 +335250,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340902,7 +340893,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340913,7 +340904,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -340924,7 +340915,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -343294,7 +343285,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -358368,7 +358359,7 @@
native="false"
synchronized="false"
static="true"
- final="false"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index 524d3f4..7214c50 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -38,7 +38,7 @@
* @hide
*/
/* package private */ class AccountAuthenticatorCache
- extends RegisteredServicesCache<AuthenticatorDescription>
+ extends RegisteredServicesCache<AuthenticatorDescription>
implements IAccountAuthenticatorCache {
private static final String TAG = "Account";
private static final MySerializer sSerializer = new MySerializer();
@@ -64,11 +64,13 @@
com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0);
final int prefId = sa.getResourceId(
com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0);
+ final boolean customTokens = sa.getBoolean(
+ com.android.internal.R.styleable.AccountAuthenticator_customTokens, false);
if (TextUtils.isEmpty(accountType)) {
return null;
}
- return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
- smallIconId, prefId);
+ return new AuthenticatorDescription(accountType, packageName, labelId, iconId,
+ smallIconId, prefId, customTokens);
} finally {
sa.recycle();
}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index fd3a0d0..6388dc5 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -188,6 +188,12 @@
public static final String KEY_ERROR_CODE = "errorCode";
public static final String KEY_ERROR_MESSAGE = "errorMessage";
public static final String KEY_USERDATA = "userdata";
+ /**
+ * Authenticators using 'customTokens' option will also get the UID of the
+ * caller
+ */
+ public static final String KEY_CALLER_UID = "callerUid";
+ public static final String KEY_CALLER_PID = "callerPid";
public static final String ACTION_AUTHENTICATOR_INTENT =
"android.accounts.AccountAuthenticator";
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index a815b3a..f19b58b 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -893,13 +893,29 @@
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final int callerUid = Binder.getCallingUid();
- final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
+ final int callerPid = Binder.getCallingPid();
+
+ AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+ mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(account.type));
+ final boolean customTokens =
+ authenticatorInfo != null && authenticatorInfo.type.customTokens;
+
+ // skip the check if customTokens
+ final boolean permissionGranted = customTokens ||
+ permissionIsGranted(account, authTokenType, callerUid);
+
+ if (customTokens) {
+ // let authenticator know the identity of the caller
+ loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
+ loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid);
+ }
long identityToken = clearCallingIdentity();
try {
// if the caller has permission, do the peek. otherwise go the more expensive
// route of starting a Session
- if (permissionGranted) {
+ if (!customTokens && permissionGranted) {
String authToken = readAuthTokenFromCache(account, authTokenType);
if (authToken != null) {
Bundle result = new Bundle();
@@ -953,8 +969,10 @@
"the type and name should not be empty");
return;
}
- saveAuthTokenToDatabase(new Account(name, type),
- authTokenType, authToken);
+ if (!customTokens) {
+ saveAuthTokenToDatabase(new Account(name, type),
+ authTokenType, authToken);
+ }
}
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
index c6515672..5d9abb0 100644
--- a/core/java/android/accounts/AuthenticatorDescription.java
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -44,9 +44,12 @@
/** The package name that can be used to lookup the resources from above. */
final public String packageName;
+ /** Authenticator handles its own token caching and permission screen */
+ final public boolean customTokens;
+
/** A constructor for a full AuthenticatorDescription */
public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
- int smallIconId, int prefId) {
+ int smallIconId, int prefId, boolean customTokens) {
if (type == null) throw new IllegalArgumentException("type cannot be null");
if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
this.type = type;
@@ -55,6 +58,12 @@
this.iconId = iconId;
this.smallIconId = smallIconId;
this.accountPreferencesId = prefId;
+ this.customTokens = customTokens;
+ }
+
+ public AuthenticatorDescription(String type, String packageName, int labelId, int iconId,
+ int smallIconId, int prefId) {
+ this(type, packageName, labelId, iconId, smallIconId, prefId, false);
}
/**
@@ -74,6 +83,7 @@
this.iconId = 0;
this.smallIconId = 0;
this.accountPreferencesId = 0;
+ this.customTokens = false;
}
private AuthenticatorDescription(Parcel source) {
@@ -83,6 +93,7 @@
this.iconId = source.readInt();
this.smallIconId = source.readInt();
this.accountPreferencesId = source.readInt();
+ this.customTokens = source.readByte() == 1;
}
/** @inheritDoc */
@@ -115,6 +126,7 @@
dest.writeInt(iconId);
dest.writeInt(smallIconId);
dest.writeInt(accountPreferencesId);
+ dest.writeByte((byte) (customTokens ? 1 : 0));
}
/** Used to create the object from a parcel. */
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index cb06c89..636f547 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -153,8 +153,9 @@
* we cache all of the current animations in this map for possible cancellation on
* another layout event.
*/
- private HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>();
- private HashMap<View, Animator> currentVisibilityAnimations = new HashMap<View, Animator>();
+ private final HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>();
+ private final HashMap<View, Animator> currentVisibilityAnimations =
+ new HashMap<View, Animator>();
/**
* This hashmap is used to track the listeners that have been added to the children of
@@ -165,7 +166,7 @@
* the process of setting up and running all appropriate animations is done, we need to
* remove these listeners and clear out the map.
*/
- private HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
+ private final HashMap<View, View.OnLayoutChangeListener> layoutChangeListenerMap =
new HashMap<View, View.OnLayoutChangeListener>();
/**
@@ -599,12 +600,14 @@
// Remove the animation from the cache when it ends
anim.addListener(new AnimatorListenerAdapter() {
private boolean canceled = false;
+ @Override
public void onAnimationCancel(Animator animator) {
// we remove canceled animations immediately, not here
canceled = true;
child.removeOnLayoutChangeListener(listener);
layoutChangeListenerMap.remove(child);
}
+ @Override
public void onAnimationEnd(Animator animator) {
if (!canceled) {
currentChangingAnimations.remove(child);
@@ -662,7 +665,8 @@
}
if (mListeners != null) {
anim.addListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd() {
+ @Override
+ public void onAnimationEnd(Animator anim) {
currentVisibilityAnimations.remove(child);
for (TransitionListener listener : mListeners) {
listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 50082f9..cfecec1 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -22,6 +22,7 @@
import android.util.AndroidRuntimeException;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
+import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
import java.util.HashMap;
@@ -478,12 +479,17 @@
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
- * @param duration The length of the animation, in milliseconds.
+ * @param duration The length of the animation, in milliseconds. This value cannot
+ * be negative.
* @return ValueAnimator The object called with setDuration(). This return
* value makes it easier to compose statements together that construct and then set the
* duration, as in <code>ValueAnimator.ofInt(0, 10).setDuration(500).start()</code>.
*/
public ValueAnimator setDuration(long duration) {
+ if (duration < 0) {
+ throw new IllegalArgumentException("Animators cannot have negative duration: " +
+ duration);
+ }
mDuration = duration;
return this;
}
@@ -829,12 +835,15 @@
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
- * @param value the interpolator to be used by this animation
+ * @param value the interpolator to be used by this animation. A value of <code>null</code>
+ * will result in linear interpolation.
*/
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
+ } else {
+ mInterpolator = new LinearInterpolator();
}
}
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 0ab987a..cccf6e8 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -26,7 +26,19 @@
/**
* Interface associated with an {@link Activity} or {@link Fragment} for managing
- * one or more {@link android.content.Loader} instances associated with it.
+ * one or more {@link android.content.Loader} instances associated with it. This
+ * helps an application manage longer-running operations in conjunction with the
+ * Activity or Fragment lifecycle; the most common use of this is with a
+ * {@link android.content.CursorLoader}, however applications are free to write
+ * their own loaders for loading other types of data.
+ *
+ * <p>As an example, here is the full implementation of a {@link Fragment}
+ * that displays a {@link android.widget.ListView} containing the results of
+ * a query against the contacts content provider. It uses a
+ * {@link android.content.CursorLoader} to manage the query on the provider.
+ *
+ * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentListCursorLoader.java
+ * fragment_cursor}
*/
public abstract class LoaderManager {
/**
@@ -49,10 +61,48 @@
* activity's state is saved. See {@link FragmentManager#openTransaction()
* FragmentManager.openTransaction()} for further discussion on this.
*
+ * <p>This function is guaranteed to be called prior to the release of
+ * the last data that was supplied for this Loader. At this point
+ * you should remove all use of the old data (since it will be released
+ * soon), but should not do your own release of the data since its Loader
+ * owns it and will take care of that. The Loader will take care of
+ * management of its data so you don't have to. In particular:
+ *
+ * <ul>
+ * <li> <p>The Loader will monitor for changes to the data, and report
+ * them to you through new calls here. You should not monitor the
+ * data yourself. For example, if the data is a {@link android.database.Cursor}
+ * and you place it in a {@link android.widget.CursorAdapter}, use
+ * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
+ * android.database.Cursor, int)} constructor <em>without</em> passing
+ * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
+ * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
+ * (that is, use 0 for the flags argument). This prevents the CursorAdapter
+ * from doing its own observing of the Cursor, which is not needed since
+ * when a change happens you will get a new Cursor throw another call
+ * here.
+ * <li> The Loader will release the data once it knows the application
+ * is no longer using it. For example, if the data is
+ * a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
+ * you should not call close() on it yourself. If the Cursor is being placed in a
+ * {@link android.widget.CursorAdapter}, you should use the
+ * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
+ * method so that the old Cursor is not closed.
+ * </ul>
+ *
* @param loader The Loader that has finished.
* @param data The data generated by the Loader.
*/
public void onLoadFinished(Loader<D> loader, D data);
+
+ /**
+ * Called when a previously created loader is being reset, and thus
+ * making its data unavailable. The application should at this point
+ * remove any references it has to the Loader's data.
+ *
+ * @param loader The Loader that is being reset.
+ */
+ public void onLoaderReset(Loader<D> loader);
}
/**
@@ -65,20 +115,33 @@
* will be called as the loader state changes. If at the point of call
* the caller is in its started state, and the requested loader
* already exists and has generated its data, then
- * callback. {@link LoaderCallbacks#onLoadFinished} will
+ * callback {@link LoaderCallbacks#onLoadFinished} will
* be called immediately (inside of this function), so you must be prepared
* for this to happen.
+ *
+ * @param id A unique identifier for this loader. Can be whatever you want.
+ * Identifiers are scoped to a particular LoaderManager instance.
+ * @param args Optional arguments to supply to the loader at construction.
+ * @param callback Interface the LoaderManager will call to report about
+ * changes in the state of the loader. Required.
*/
public abstract <D> Loader<D> initLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback);
/**
- * Creates a new loader in this manager, registers the callbacks to it,
+ * Starts a new or restarts an existing {@link android.content.Loader} in
+ * this manager, registers the callbacks to it,
* and (if the activity/fragment is currently started) starts loading it.
* If a loader with the same id has previously been
* started it will automatically be destroyed when the new loader completes
* its work. The callback will be delivered before the old loader
* is destroyed.
+ *
+ * @param id A unique identifier for this loader. Can be whatever you want.
+ * Identifiers are scoped to a particular LoaderManager instance.
+ * @param args Optional arguments to supply to the loader at construction.
+ * @param callback Interface the LoaderManager will call to report about
+ * changes in the state of the loader. Required.
*/
public abstract <D> Loader<D> restartLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<D> callback);
@@ -86,7 +149,15 @@
/**
* Stops and removes the loader with the given ID.
*/
- public abstract void stopLoader(int id);
+ public abstract void destroyLoader(int id);
+
+ /**
+ * @deprecated Renamed to {@link #destroyLoader}.
+ */
+ @Deprecated
+ public void stopLoader(int id) {
+ destroyLoader(id);
+ }
/**
* Return the Loader with the given id or null if no matching Loader
@@ -132,6 +203,7 @@
Loader<Object> mLoader;
Object mData;
boolean mStarted;
+ boolean mNeedReset;
boolean mRetaining;
boolean mRetainingStarted;
boolean mDestroyed;
@@ -191,13 +263,16 @@
stop();
}
}
- if (mStarted && mData != null) {
- // This loader was retained, and now at the point of
- // finishing the retain we find we remain started, have
- // our data, and the owner has a new callback... so
- // let's deliver the data now.
- callOnLoadFinished(mLoader, mData);
- }
+ }
+
+ if (mStarted && mData != null) {
+ // This loader has retained its data, either completely across
+ // a configuration change or just whatever the last data set
+ // was after being restarted from a stop, and now at the point of
+ // finishing the retain we find we remain started, have
+ // our data, and the owner has a new callback... so
+ // let's deliver the data now.
+ callOnLoadFinished(mLoader, mData);
}
}
@@ -211,20 +286,35 @@
mLoader.unregisterListener(this);
mLoader.stopLoading();
}
- mData = null;
}
}
void destroy() {
if (DEBUG) Log.v(TAG, " Destroying: " + this);
mDestroyed = true;
+ if (mCallbacks != null && mLoader != null && mData != null && mNeedReset) {
+ String lastBecause = null;
+ if (mActivity != null) {
+ lastBecause = mActivity.mFragments.mNoTransactionsBecause;
+ mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
+ }
+ try {
+ mCallbacks.onLoaderReset(mLoader);
+ } finally {
+ if (mActivity != null) {
+ mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ }
+ }
+ }
+ mNeedReset = false;
mCallbacks = null;
+ mData = null;
if (mLoader != null) {
if (mListenerRegistered) {
mListenerRegistered = false;
mLoader.unregisterListener(this);
}
- mLoader.destroy();
+ mLoader.reset();
}
}
@@ -238,7 +328,9 @@
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
mData = data;
- callOnLoadFinished(loader, data);
+ if (mStarted) {
+ callOnLoadFinished(loader, data);
+ }
if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this);
@@ -248,6 +340,7 @@
// clean it up.
LoaderInfo info = mInactiveLoaders.get(mId);
if (info != null && info != this) {
+ info.mNeedReset = false;
info.destroy();
mInactiveLoaders.remove(mId);
}
@@ -267,6 +360,7 @@
mActivity.mFragments.mNoTransactionsBecause = lastBecause;
}
}
+ mNeedReset = true;
}
}
@@ -366,6 +460,7 @@
// yet destroyed the last inactive loader. So just do
// that now.
if (DEBUG) Log.v(TAG, " Removing last inactive loader in " + this);
+ inactive.mNeedReset = false;
inactive.destroy();
mInactiveLoaders.put(id, info);
} else {
@@ -388,7 +483,7 @@
return (Loader<D>)info.mLoader;
}
- public void stopLoader(int id) {
+ public void destroyLoader(int id) {
if (DEBUG) Log.v(TAG, "stopLoader in " + this + " of " + id);
int idx = mLoaders.indexOfKey(id);
if (idx >= 0) {
@@ -416,12 +511,13 @@
return;
}
+ mStarted = true;
+
// Call out to sub classes so they can start their loaders
// Let the existing loaders know that we want to be notified when a load is complete
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).start();
}
- mStarted = true;
}
void doStop() {
diff --git a/core/java/android/app/LoaderManagingFragment.java b/core/java/android/app/LoaderManagingFragment.java
deleted file mode 100644
index f0f5856..0000000
--- a/core/java/android/app/LoaderManagingFragment.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.content.Loader;
-import android.os.Bundle;
-
-import java.util.HashMap;
-
-/**
- * A Fragment that has utility methods for managing {@link Loader}s.
- *
- * @param <D> The type of data returned by the Loader. If you're using multiple Loaders with
- * different return types use Object and case the results.
- *
- * @deprecated This was an old design, it will be removed before Honeycomb ships.
- */
-@Deprecated
-public abstract class LoaderManagingFragment<D> extends Fragment
- implements Loader.OnLoadCompleteListener<D> {
- private boolean mStarted = false;
-
- static final class LoaderInfo<D> {
- public Bundle args;
- public Loader<D> loader;
- }
- private HashMap<Integer, LoaderInfo<D>> mLoaders;
- private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
-
- /**
- * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
- * If a loader with the same id has previously been started it will automatically be destroyed
- * when the new loader completes it's work. The callback will be delivered before the old loader
- * is destroyed.
- */
- public Loader<D> startLoading(int id, Bundle args) {
- LoaderInfo<D> info = mLoaders.get(id);
- if (info != null) {
- // Keep track of the previous instance of this loader so we can destroy
- // it when the new one completes.
- mInactiveLoaders.put(id, info);
- }
- info = new LoaderInfo<D>();
- info.args = args;
- mLoaders.put(id, info);
- Loader<D> loader = onCreateLoader(id, args);
- info.loader = loader;
- if (mStarted) {
- // The activity will start all existing loaders in it's onStart(), so only start them
- // here if we're past that point of the activitiy's life cycle
- loader.registerListener(id, this);
- loader.startLoading();
- }
- return loader;
- }
-
- protected abstract Loader<D> onCreateLoader(int id, Bundle args);
- protected abstract void onInitializeLoaders();
- protected abstract void onLoadFinished(Loader<D> loader, D data);
-
- public final void onLoadComplete(Loader<D> loader, D data) {
- // Notify of the new data so the app can switch out the old data before
- // we try to destroy it.
- onLoadFinished(loader, data);
-
- // Look for an inactive loader and destroy it if found
- int id = loader.getId();
- LoaderInfo<D> info = mInactiveLoaders.get(id);
- if (info != null) {
- Loader<D> oldLoader = info.loader;
- if (oldLoader != null) {
- oldLoader.destroy();
- }
- mInactiveLoaders.remove(id);
- }
- }
-
- @Override
- public void onCreate(Bundle savedState) {
- super.onCreate(savedState);
-
- if (mLoaders == null) {
- // Look for a passed along loader and create a new one if it's not there
-// TODO: uncomment once getLastNonConfigurationInstance method is available
-// mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
- if (mLoaders == null) {
- mLoaders = new HashMap<Integer, LoaderInfo<D>>();
- onInitializeLoaders();
- }
- }
- if (mInactiveLoaders == null) {
- mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
- }
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- // Call out to sub classes so they can start their loaders
- // Let the existing loaders know that we want to be notified when a load is complete
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- int id = entry.getKey();
- if (loader == null) {
- loader = onCreateLoader(id, info.args);
- info.loader = loader;
- }
- loader.registerListener(id, this);
- loader.startLoading();
- }
-
- mStarted = true;
- }
-
- @Override
- public void onStop() {
- super.onStop();
-
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- if (loader == null) {
- continue;
- }
-
- // Let the loader know we're done with it
- loader.unregisterListener(this);
-
- // The loader isn't getting passed along to the next instance so ask it to stop loading
- if (!getActivity().isChangingConfigurations()) {
- loader.stopLoading();
- }
- }
-
- mStarted = false;
- }
-
- /* TO DO: This needs to be turned into a retained fragment.
- @Override
- public Object onRetainNonConfigurationInstance() {
- // Pass the loader along to the next guy
- Object result = mLoaders;
- mLoaders = null;
- return result;
- }
- **/
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- if (mLoaders != null) {
- for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
- LoaderInfo<D> info = entry.getValue();
- Loader<D> loader = info.loader;
- if (loader == null) {
- continue;
- }
- loader.destroy();
- }
- }
- }
-
- /**
- * Stops and removes the loader with the given ID.
- */
- public void stopLoading(int id) {
- if (mLoaders != null) {
- LoaderInfo<D> info = mLoaders.remove(id);
- if (info != null) {
- Loader<D> loader = info.loader;
- if (loader != null) {
- loader.unregisterListener(this);
- loader.destroy();
- }
- }
- }
- }
-
- /**
- * @return the Loader with the given id or null if no matching Loader
- * is found.
- */
- public Loader<D> getLoader(int id) {
- LoaderInfo<D> loaderInfo = mLoaders.get(id);
- if (loaderInfo != null) {
- return mLoaders.get(id).loader;
- }
- return null;
- }
-}
diff --git a/core/java/android/content/CursorLoader.java b/core/java/android/content/CursorLoader.java
index 7776874..c73f95d 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -27,6 +27,7 @@
Cursor mCursor;
ForceLoadContentObserver mObserver;
boolean mStopped;
+ boolean mReset;
Uri mUri;
String[] mProjection;
String mSelection;
@@ -57,7 +58,7 @@
/* Runs on the UI thread */
@Override
public void deliverResult(Cursor cursor) {
- if (mStopped) {
+ if (mReset) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
@@ -66,9 +67,12 @@
}
Cursor oldCursor = mCursor;
mCursor = cursor;
- super.deliverResult(cursor);
- if (oldCursor != null && !oldCursor.isClosed()) {
+ if (!mStopped) {
+ super.deliverResult(cursor);
+ }
+
+ if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
@@ -94,6 +98,7 @@
@Override
public void startLoading() {
mStopped = false;
+ mReset = false;
if (mCursor != null) {
deliverResult(mCursor);
@@ -107,11 +112,6 @@
*/
@Override
public void stopLoading() {
- if (mCursor != null && !mCursor.isClosed()) {
- mCursor.close();
- }
- mCursor = null;
-
// Attempt to cancel the current load task if possible.
cancelLoad();
@@ -127,9 +127,16 @@
}
@Override
- public void destroy() {
+ public void reset() {
+ mReset = true;
+
// Ensure the loader is stopped
stopLoading();
+
+ if (mCursor != null && !mCursor.isClosed()) {
+ mCursor.close();
+ }
+ mCursor = null;
}
public Uri getUri() {
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 234096a..73d7103 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -128,7 +128,7 @@
* the data set and may deliver future callbacks if the source changes. Calling
* {@link #stopLoading} will stop the delivery of callbacks.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public abstract void startLoading();
@@ -141,22 +141,34 @@
/**
* Stops delivery of updates until the next time {@link #startLoading()} is called
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public abstract void stopLoading();
/**
- * Destroys the loader and frees its resources, making it unusable.
+ * Resets the state of the Loader. The Loader should at this point free
+ * all of its resources, since it may never be called again; however, its
+ * {@link #startLoading()} may later be called at which point it must be
+ * able to start running again.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
- public abstract void destroy();
+ public void reset() {
+ destroy();
+ }
+
+ /**
+ * @deprecated Old API, implement reset() now.
+ */
+ @Deprecated
+ public void destroy() {
+ }
/**
* Called when {@link ForceLoadContentObserver} detects a change. Calls {@link #forceLoad()}
* by default.
*
- * Must be called from the UI thread
+ * <p>Must be called from the UI thread
*/
public void onContentChanged() {
forceLoad();
diff --git a/core/java/android/content/SyncActivityTooManyDeletes.java b/core/java/android/content/SyncActivityTooManyDeletes.java
new file mode 100644
index 0000000..350f35e
--- /dev/null
+++ b/core/java/android/content/SyncActivityTooManyDeletes.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import com.android.internal.R;
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Presents multiple options for handling the case where a sync was aborted because there
+ * were too many pending deletes. One option is to force the delete, another is to rollback
+ * the deletes, the third is to do nothing.
+ * @hide
+ */
+public class SyncActivityTooManyDeletes extends Activity
+ implements AdapterView.OnItemClickListener {
+
+ private long mNumDeletes;
+ private Account mAccount;
+ private String mAuthority;
+ private String mProvider;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ finish();
+ return;
+ }
+
+ mNumDeletes = extras.getLong("numDeletes");
+ mAccount = (Account) extras.getParcelable("account");
+ mAuthority = extras.getString("authority");
+ mProvider = extras.getString("provider");
+
+ // the order of these must match up with the constants for position used in onItemClick
+ CharSequence[] options = new CharSequence[]{
+ getResources().getText(R.string.sync_really_delete),
+ getResources().getText(R.string.sync_undo_deletes),
+ getResources().getText(R.string.sync_do_nothing)
+ };
+
+ ListAdapter adapter = new ArrayAdapter<CharSequence>(this,
+ android.R.layout.simple_list_item_1,
+ android.R.id.text1,
+ options);
+
+ ListView listView = new ListView(this);
+ listView.setAdapter(adapter);
+ listView.setItemsCanFocus(true);
+ listView.setOnItemClickListener(this);
+
+ TextView textView = new TextView(this);
+ CharSequence tooManyDeletesDescFormat =
+ getResources().getText(R.string.sync_too_many_deletes_desc);
+ textView.setText(String.format(tooManyDeletesDescFormat.toString(),
+ mNumDeletes, mProvider, mAccount.name));
+
+ final LinearLayout ll = new LinearLayout(this);
+ ll.setOrientation(LinearLayout.VERTICAL);
+ final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0);
+ ll.addView(textView, lp);
+ ll.addView(listView, lp);
+
+ // TODO: consider displaying the icon of the account type
+// AuthenticatorDescription[] descs = AccountManager.get(this).getAuthenticatorTypes();
+// for (AuthenticatorDescription desc : descs) {
+// if (desc.type.equals(mAccount.type)) {
+// try {
+// final Context authContext = createPackageContext(desc.packageName, 0);
+// ImageView imageView = new ImageView(this);
+// imageView.setImageDrawable(authContext.getResources().getDrawable(desc.iconId));
+// ll.addView(imageView, lp);
+// } catch (PackageManager.NameNotFoundException e) {
+// }
+// break;
+// }
+// }
+
+ setContentView(ll);
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // the constants for position correspond to the items options array in onCreate()
+ if (position == 0) startSyncReallyDelete();
+ else if (position == 1) startSyncUndoDeletes();
+ finish();
+ }
+
+ private void startSyncReallyDelete() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+ ContentResolver.requestSync(mAccount, mAuthority, extras);
+ }
+
+ private void startSyncUndoDeletes() {
+ Bundle extras = new Bundle();
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
+ ContentResolver.requestSync(mAccount, mAuthority, extras);
+ }
+}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 599429b..8b292c9 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -2157,9 +2157,7 @@
}
CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
- Intent clickIntent = new Intent();
- clickIntent.setClassName("com.android.providers.subscribedfeeds",
- "com.android.settings.SyncActivityTooManyDeletes");
+ Intent clickIntent = new Intent(mContext, SyncActivityTooManyDeletes.class);
clickIntent.putExtra("account", account);
clickIntent.putExtra("authority", authority);
clickIntent.putExtra("provider", authorityName.toString());
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 0f96d6b..5d222d9 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -24,7 +24,7 @@
interface INfcTag
{
int close(int nativeHandle);
- int connect(int nativeHandle);
+ int connect(int nativeHandle, int technology);
int reconnect(int nativeHandle);
int[] getTechList(int nativeHandle);
byte[] getUid(int nativeHandle);
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index d042634..7404950 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -63,6 +63,8 @@
/*package*/ final Bundle[] mTechExtras;
/*package*/ final int mServiceHandle; // for use by NFC service, 0 indicates a mock
+ /*package*/ int mConnectedTechnology;
+
/**
* Hidden constructor to be used by NFC service and internal classes.
* @hide
@@ -76,6 +78,8 @@
// Ensure mTechExtras is as long as mTechList
mTechExtras = Arrays.copyOf(techListExtras, techList.length);
mServiceHandle = serviceHandle;
+
+ mConnectedTechnology = -1;
}
/**
@@ -244,4 +248,29 @@
return new Tag[size];
}
};
+
+ /*
+ * @hide
+ */
+ public synchronized void setConnectedTechnology(int technology) {
+ if (mConnectedTechnology == -1) {
+ mConnectedTechnology = technology;
+ } else {
+ throw new IllegalStateException("Close other technology first!");
+ }
+ }
+
+ /*
+ * @hide
+ */
+ public int getConnectedTechnology() {
+ return mConnectedTechnology;
+ }
+
+ /*
+ * @hide
+ */
+ public void setTechnologyDisconnected() {
+ mConnectedTechnology = -1;
+ }
}
diff --git a/core/java/android/nfc/technology/BasicTagTechnology.java b/core/java/android/nfc/technology/BasicTagTechnology.java
index a50c10b..553f6ec 100644
--- a/core/java/android/nfc/technology/BasicTagTechnology.java
+++ b/core/java/android/nfc/technology/BasicTagTechnology.java
@@ -22,6 +22,7 @@
import android.nfc.INfcTag;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
+import android.nfc.ErrorCodes;
import android.os.RemoteException;
import android.util.Log;
@@ -101,6 +102,13 @@
return mTag;
}
+ public void checkConnected() {
+ if ((mTag.getConnectedTechnology() != getTechnologyId()) ||
+ (mTag.getConnectedTechnology() == -1)) {
+ throw new IllegalStateException("Call connect() first!");
+ }
+ }
+
/**
* <p>Requires {@link android.Manifest.permission#NFC} permission.
*/
@@ -144,8 +152,53 @@
*/
@Override
public void connect() throws IOException {
- //TODO(nxp): enforce exclusivity
- mIsConnected = true;
+ try {
+ int errorCode = mTagService.connect(mTag.getServiceHandle(), getTechnologyId());
+
+ if (errorCode == ErrorCodes.SUCCESS) {
+ // Store this in the tag object
+ mTag.setConnectedTechnology(getTechnologyId());
+ mIsConnected = true;
+ } else {
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ throw new IOException("NFC service died");
+ }
+ }
+
+ /**
+ * Re-connect to the {@link Tag} associated with this connection.
+ * <p>
+ * Reconnecting to a tag can be used to reset the state of the tag itself.
+ * This method blocks until the connection is re-established.
+ * <p>
+ * {@link #close} can be called from another thread to cancel this connection
+ * attempt.
+ * <p>Requires {@link android.Manifest.permission#NFC} permission.
+ * @throws IOException if the target is lost, or connect canceled
+ */
+ @Override
+ public void reconnect() throws IOException {
+ if (!mIsConnected) {
+ throw new IllegalStateException("Technology not connected yet");
+ } else {
+ try {
+ int errorCode = mTagService.reconnect(mTag.getServiceHandle());
+
+ if (errorCode != ErrorCodes.SUCCESS) {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ throw new IOException();
+ }
+ } catch (RemoteException e) {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
+ attemptDeadServiceRecovery(e);
+ throw new IOException("NFC service died");
+ }
+ }
}
/**
@@ -160,7 +213,6 @@
*/
@Override
public void close() {
- mIsConnected = false;
try {
/* Note that we don't want to physically disconnect the tag,
* but just reconnect to it to reset its state
@@ -168,6 +220,9 @@
mTagService.reconnect(mTag.getServiceHandle());
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
+ } finally {
+ mIsConnected = false;
+ mTag.setTechnologyDisconnected();
}
}
@@ -183,6 +238,8 @@
* @throws IOException if the target is lost or connection closed
*/
public byte[] transceive(byte[] data) throws IOException {
+ checkConnected();
+
try {
byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, true);
if (response == null) {
diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java
index 5346c67..118bff7 100644
--- a/core/java/android/nfc/technology/IsoDep.java
+++ b/core/java/android/nfc/technology/IsoDep.java
@@ -75,6 +75,8 @@
* @throws IOException, UnsupportedOperationException
*/
public void selectAid(byte[] aid) throws IOException, UnsupportedOperationException {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java
index defdcf2..d5f0a31 100644
--- a/core/java/android/nfc/technology/MifareClassic.java
+++ b/core/java/android/nfc/technology/MifareClassic.java
@@ -229,6 +229,8 @@
* Authenticate for a given sector.
*/
public boolean authenticateSector(int sector, byte[] key, boolean keyA) {
+ checkConnected();
+
byte[] cmd = new byte[12];
// First byte is the command
@@ -264,6 +266,8 @@
* @throws IOException
*/
public byte[] readBlock(int sector, int block) throws IOException {
+ checkConnected();
+
byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
byte[] blockread_cmd = { 0x30, addr }; // phHal_eMifareRead
@@ -300,6 +304,8 @@
*/
@Override
public byte[] transceive(byte[] data) throws IOException {
+ checkConnected();
+
try {
byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
if (response == null) {
diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java
index 58d2645..7103b4d 100644
--- a/core/java/android/nfc/technology/MifareUltralight.java
+++ b/core/java/android/nfc/technology/MifareUltralight.java
@@ -66,11 +66,44 @@
* @throws IOException
*/
public byte[] readBlock(int block) throws IOException {
+ checkConnected();
+
byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead
return transceive(blockread_cmd);
}
/**
+ * @throws IOException
+ */
+ public byte[] readOTP() throws IOException {
+ checkConnected();
+
+ return readBlock(3); // OTP is at page 3
+ }
+
+ public void writePage(int block, byte[] data) throws IOException {
+ checkConnected();
+
+ byte[] pagewrite_cmd = new byte[data.length + 2];
+ pagewrite_cmd[0] = (byte) 0xA2;
+ pagewrite_cmd[1] = (byte) block;
+ System.arraycopy(data, 0, pagewrite_cmd, 2, data.length);
+
+ transceive(pagewrite_cmd);
+ }
+
+ public void writeBlock(int block, byte[] data) throws IOException {
+ checkConnected();
+
+ byte[] blockwrite_cmd = new byte[data.length + 2];
+ blockwrite_cmd[0] = (byte) 0xA0;
+ blockwrite_cmd[1] = (byte) block;
+ System.arraycopy(data, 0, blockwrite_cmd, 2, data.length);
+
+ transceive(blockwrite_cmd);
+ }
+
+ /**
* Send data to a tag and receive the response.
* <p>
* This method will block until the response is received. It can be canceled
@@ -83,6 +116,8 @@
*/
@Override
public byte[] transceive(byte[] data) throws IOException {
+ checkConnected();
+
try {
byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
if (response == null) {
@@ -95,12 +130,4 @@
}
}
- /**
- * @throws IOException
- */
- /*
- public byte[] readOTP();
- public void writePage(int block, byte[] data);
- public void writeBlock(int block, byte[] data);
- */
}
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
index 7e194aa..c45c97d 100644
--- a/core/java/android/nfc/technology/Ndef.java
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -113,6 +113,8 @@
* and requires a connection.
*/
public NdefMessage getNdefMessage() throws IOException, FormatException {
+ checkConnected();
+
try {
int serviceHandle = mTag.getServiceHandle();
if (mTagService.isNdef(serviceHandle)) {
@@ -143,6 +145,8 @@
* @throws IOException
*/
public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
+ checkConnected();
+
try {
int errorCode = mTagService.ndefWrite(mTag.getServiceHandle(), msg);
switch (errorCode) {
@@ -172,6 +176,8 @@
* @throws IOException
*/
public void writeExtraNdefMessage(int i, NdefMessage msg) throws IOException, FormatException {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
@@ -180,6 +186,8 @@
* @throws IOException
*/
public boolean makeReadonly() throws IOException {
+ checkConnected();
+
try {
int errorCode = mTagService.ndefMakeReadOnly(mTag.getServiceHandle());
switch (errorCode) {
@@ -205,11 +213,15 @@
* For NFC Forum Type 1 and 2 only.
*/
public void makeLowLevelReadonly() {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
@Override
public byte[] transceive(byte[] data) {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/technology/NdefFormatable.java
index bd21e58..899b95f 100644
--- a/core/java/android/nfc/technology/NdefFormatable.java
+++ b/core/java/android/nfc/technology/NdefFormatable.java
@@ -49,7 +49,9 @@
* NdefFormatable#format(NdefMessage)}
*/
public boolean canBeFormatted() throws IOException {
- throw new UnsupportedOperationException();
+ checkConnected();
+
+ throw new UnsupportedOperationException();
}
/**
@@ -57,6 +59,8 @@
* NdefMessage to be written on the tag.
*/
public void format(NdefMessage firstMessage) throws IOException, FormatException {
+ checkConnected();
+
try {
byte[] DEFAULT_KEY = {(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF};
@@ -97,6 +101,8 @@
@Override
public byte[] transceive(byte[] data) {
+ checkConnected();
+
throw new UnsupportedOperationException();
}
}
diff --git a/core/java/android/nfc/technology/TagTechnology.java b/core/java/android/nfc/technology/TagTechnology.java
index bef1cc4..62216c1 100644
--- a/core/java/android/nfc/technology/TagTechnology.java
+++ b/core/java/android/nfc/technology/TagTechnology.java
@@ -82,6 +82,11 @@
public void connect() throws IOException;
/**
+ * @throws IOException
+ */
+ public void reconnect() throws IOException;
+
+ /**
* Non-blocking. Immediately causes all blocking calls
* to throw IOException.
*/
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 507cfab..0c6ab9e 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -98,6 +98,12 @@
public static final int NFC_UID = 1022;
/**
+ * Defines the GID for the group that allows write access to the internal media storage.
+ * @hide
+ */
+ public static final int MEDIA_RW_GID = 1023;
+
+ /**
* Defines the start of a range of UIDs (and GIDs), going from this
* number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
* to applications.
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index d197341..f91db87 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1514,7 +1514,7 @@
}
handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
- if (connectPanDeviceNative(objectPath, "nap", "panu")) {
+ if (connectPanDeviceNative(objectPath, "nap")) {
log ("connecting to PAN");
return true;
} else {
@@ -2885,7 +2885,7 @@
private native boolean disconnectInputDeviceNative(String path);
private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
- private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
+ private native boolean connectPanDeviceNative(String path, String dstRole);
private native boolean disconnectPanDeviceNative(String path);
private native int[] addReservedServiceRecordsNative(int[] uuuids);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d27e99d..75aebc9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8206,8 +8206,9 @@
/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
- * called. When implementing a view, do not override this method; instead,
- * you should implement {@link #onDraw}.
+ * called. When implementing a view, implement {@link #onDraw} instead of
+ * overriding this method. If you do need to override this method, call
+ * the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5385cd9..8446a8f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -576,14 +576,16 @@
/**
* Set the width and height layout parameters of the window. The default
- * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT to
- * make a window that is not full-screen.
+ * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT
+ * or an absolute value to make a window that is not full-screen.
*
* @param width The desired layout width of the window.
* @param height The desired layout height of the window.
+ *
+ * @see ViewGroup.LayoutParams#height
+ * @see ViewGroup.LayoutParams#width
*/
- public void setLayout(int width, int height)
- {
+ public void setLayout(int width, int height) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.width = width;
attrs.height = height;
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 821bcc6..99be64b 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -44,7 +44,10 @@
* this component and if they can not be resolved by the cache, the HTTP headers
* are attached, as appropriate, to the request for revalidation of content. The
* class also manages the cache size.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+@Deprecated
public final class CacheManager {
private static final String LOGTAG = "cache";
@@ -85,7 +88,10 @@
* This class represents a resource retrieved from the HTTP cache.
* Instances of this class can be obtained by invoking the
* CacheManager.getCacheFile() method.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static class CacheResult {
// these fields are saved to the database
int httpStatusCode;
@@ -167,6 +173,13 @@
public void setEncoding(String encoding) {
this.encoding = encoding;
}
+
+ /**
+ * @hide
+ */
+ public void setContentLength(long contentLength) {
+ this.contentLength = contentLength;
+ }
}
/**
@@ -213,9 +226,12 @@
/**
* get the base directory of the cache. With localPath of the CacheResult,
* it identifies the cache file.
- *
+ *
* @return File The base directory of the cache.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static File getCacheFileBaseDir() {
return mBaseDir;
}
@@ -237,9 +253,12 @@
/**
* get the state of the current cache, enabled or disabled
- *
+ *
* @return return if it is disabled
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static boolean cacheDisabled() {
return mDisabled;
}
@@ -307,8 +326,11 @@
* HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in the
* cached headers.
*
- * @return the CacheResult for a given url
+ * @return the CacheResult for a given url.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static CacheResult getCacheFile(String url,
Map<String, String> headers) {
return getCacheFile(url, 0, headers);
@@ -383,7 +405,10 @@
* @return CacheResult for a given url
* @hide - hide createCacheFile since it has a parameter of type headers, which is
* in a hidden package.
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static CacheResult createCacheFile(String url, int statusCode,
Headers headers, String mimeType, boolean forceCache) {
return createCacheFile(url, statusCode, headers, mimeType, 0,
@@ -448,7 +473,10 @@
/**
* Save the info of a cache file for a given url to the CacheMap so that it
* can be reused later
+ *
+ * @deprecated Access to the HTTP cache will be removed in a future release.
*/
+ @Deprecated
public static void saveCacheFile(String url, CacheResult cacheRet) {
saveCacheFile(url, 0, cacheRet);
}
diff --git a/core/java/android/webkit/SelectActionModeCallback.java b/core/java/android/webkit/SelectActionModeCallback.java
index 54c9d9a..86a67c7 100644
--- a/core/java/android/webkit/SelectActionModeCallback.java
+++ b/core/java/android/webkit/SelectActionModeCallback.java
@@ -34,7 +34,11 @@
}
void finish() {
- mActionMode.finish();
+ // It is possible that onCreateActionMode was never called, in the case
+ // where there is no ActionBar, for example.
+ if (mActionMode != null) {
+ mActionMode.finish();
+ }
}
// ActionMode.Callback implementation
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7195f98..05bb19d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3790,7 +3790,9 @@
public boolean selectText() {
int x = viewToContentX((int) mLastTouchX + mScrollX);
int y = viewToContentY((int) mLastTouchY + mScrollY);
- setUpSelect();
+ if (!setUpSelect()) {
+ return false;
+ }
if (mNativeClass != 0 && nativeWordSelection(x, y)) {
nativeSetExtendSelection();
mDrawSelectionPointer = false;
@@ -4675,10 +4677,15 @@
return false;
}
- private void setUpSelect() {
- if (0 == mNativeClass) return; // client isn't initialized
- if (inFullScreenMode()) return;
- if (mSelectingText) return;
+ /*
+ * Enter selecting text mode. Returns true if the WebView is now in
+ * selecting text mode (including if it was already in that mode, and this
+ * method did nothing).
+ */
+ private boolean setUpSelect() {
+ if (0 == mNativeClass) return false; // client isn't initialized
+ if (inFullScreenMode()) return false;
+ if (mSelectingText) return true;
mExtendSelection = false;
mSelectingText = mDrawSelectionPointer = true;
// don't let the picture change during text selection
@@ -4698,7 +4705,13 @@
nativeHideCursor();
mSelectCallback = new SelectActionModeCallback();
mSelectCallback.setWebView(this);
- startActionMode(mSelectCallback);
+ if (startActionMode(mSelectCallback) == null) {
+ // There is no ActionMode, so do not allow the user to modify a
+ // selection.
+ selectionDone();
+ return false;
+ }
+ return true;
}
/**
@@ -4715,7 +4728,9 @@
void selectAll() {
if (0 == mNativeClass) return; // client isn't initialized
if (inFullScreenMode()) return;
- if (!mSelectingText) setUpSelect();
+ if (!mSelectingText && !setUpSelect()) {
+ return;
+ }
nativeSelectAll();
mDrawSelectionPointer = false;
mExtendSelection = true;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index b8c8913..fdd0710 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1991,6 +1991,13 @@
.obtainMessage(WebCoreThread.RESUME_PRIORITY));
}
+ static void sendStaticMessage(int messageType, Object argument) {
+ if (sWebCoreHandler == null)
+ return;
+
+ sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
+ }
+
static void pauseUpdatePicture(WebViewCore core) {
// Note: there is one possible failure mode. If pauseUpdatePicture() is
// called from UI thread while WEBKIT_DRAW is just pulled out of the
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 4cf8785..516162a 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -67,7 +67,7 @@
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
- protected DataSetObserver mDataSetObserver = new MyDataSetObserver();
+ protected DataSetObserver mDataSetObserver;
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -81,54 +81,81 @@
/**
* If set the adapter will call requery() on the cursor whenever a content change
- * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*/
+ @Deprecated
public static final int FLAG_AUTO_REQUERY = 0x01;
/**
* If set the adapter will register a content observer on the cursor and will call
- * {@link #onContentChanged()} when a notification comes in.
+ * {@link #onContentChanged()} when a notification comes in. Be careful when
+ * using this flag: you will need to unset the current Cursor from the adapter
+ * to avoid leaks due to its registered observers. This flag is not needed
+ * when using a CursorAdapter with a
+ * {@link android.content.CursorLoader}.
*/
public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
/**
- * Constructor. The adapter will call requery() on the cursor whenever
- * it changes so that the most recent data is always displayed.
+ * Constructor that always enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param c The cursor from which to get the data.
* @param context The context
*/
+ @Deprecated
public CursorAdapter(Context context, Cursor c) {
init(context, c, FLAG_AUTO_REQUERY);
}
/**
- * Constructor
+ * Constructor that allows control over auto-requery. It is recommended
+ * you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
+ * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * will always be set.
+ *
* @param c The cursor from which to get the data.
* @param context The context
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
- * data is always displayed.
+ * data is always displayed. Using true here is discouraged.
*/
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
/**
- * Constructor
+ * Recommended constructor.
+ *
* @param c The cursor from which to get the data.
* @param context The context
- * @param flags flags used to determine the behavior of the adapter
+ * @param flags Flags used to determine the behavior of the adapter; may
+ * be any combination of {@link #FLAG_AUTO_REQUERY} and
+ * {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
*/
public CursorAdapter(Context context, Cursor c, int flags) {
init(context, c, flags);
}
+ /**
+ * @deprecated Don't use this, use the normal constructor. This will
+ * be removed in the future.
+ */
+ @Deprecated
protected void init(Context context, Cursor c, boolean autoRequery) {
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
}
- protected void init(Context context, Cursor c, int flags) {
+ void init(Context context, Cursor c, int flags) {
if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
flags |= FLAG_REGISTER_CONTENT_OBSERVER;
mAutoRequery = true;
@@ -142,13 +169,15 @@
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
mChangeObserver = new ChangeObserver();
+ mDataSetObserver = new MyDataSetObserver();
} else {
mChangeObserver = null;
+ mDataSetObserver = null;
}
if (cursorPresent) {
if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
- c.registerDataSetObserver(mDataSetObserver);
+ if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
}
}
@@ -275,22 +304,39 @@
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*
- * @param cursor the new cursor to be used
+ * @param cursor The new cursor to be used
*/
public void changeCursor(Cursor cursor) {
- if (cursor == mCursor) {
- return;
+ Cursor old = swapCursor(cursor);
+ if (old != null) {
+ old.close();
}
- if (mCursor != null) {
- if (mChangeObserver != null) mCursor.unregisterContentObserver(mChangeObserver);
- mCursor.unregisterDataSetObserver(mDataSetObserver);
- mCursor.close();
+ }
+
+ /**
+ * Swap in a new Cursor, returning the old Cursor. Unlike
+ * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
+ * closed.
+ *
+ * @param newCursor The new cursor to be used.
+ * @return Returns the previously set Cursor, or null if there wasa not one.
+ * If the given new Cursor is the same instance is the previously set
+ * Cursor, null is also returned.
+ */
+ public Cursor swapCursor(Cursor newCursor) {
+ if (newCursor == mCursor) {
+ return null;
}
- mCursor = cursor;
- if (cursor != null) {
- if (mChangeObserver != null) cursor.registerContentObserver(mChangeObserver);
- cursor.registerDataSetObserver(mDataSetObserver);
- mRowIDColumn = cursor.getColumnIndexOrThrow("_id");
+ Cursor oldCursor = mCursor;
+ if (oldCursor != null) {
+ if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
+ if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
+ }
+ mCursor = newCursor;
+ if (newCursor != null) {
+ if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
+ if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
+ mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
@@ -300,6 +346,7 @@
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
+ return oldCursor;
}
/**
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7b35b51..2d9a5f9 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -979,10 +979,7 @@
// children
// after we have completed drawing ourselves.
- // Draw the selector wheel if needed
- if (mDrawSelectorWheel) {
- super.draw(canvas);
- }
+ super.draw(canvas);
// Draw our children if we are not showing the selector wheel of fading
// it out
diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java
index aee411e..7341c2c 100644
--- a/core/java/android/widget/ResourceCursorAdapter.java
+++ b/core/java/android/widget/ResourceCursorAdapter.java
@@ -35,13 +35,19 @@
private LayoutInflater mInflater;
/**
- * Constructor.
+ * Constructor the enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
*/
+ @Deprecated
public ResourceCursorAdapter(Context context, int layout, Cursor c) {
super(context, c);
mLayout = mDropDownLayout = layout;
@@ -49,7 +55,11 @@
}
/**
- * Constructor.
+ * Constructor with default behavior as per
+ * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
+ * you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
+ * When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
+ * will always be set.
*
* @param context The context where the ListView associated with this adapter is running
* @param layout resource identifier of a layout file that defines the views
@@ -58,7 +68,7 @@
* @param c The cursor from which to get the data.
* @param autoRequery If true the adapter will call requery() on the
* cursor whenever it changes so the most recent
- * data is always displayed.
+ * data is always displayed. Using true here is discouraged.
*/
public ResourceCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
@@ -67,14 +77,15 @@
}
/**
- * Constructor.
+ * Standard constructor.
*
* @param context The context where the ListView associated with this adapter is running
- * @param layout resource identifier of a layout file that defines the views
+ * @param layout Resource identifier of a layout file that defines the views
* for this list item. Unless you override them later, this will
* define both the item views and the drop down views.
* @param c The cursor from which to get the data.
- * @param flags flags used to determine the behavior of the adapter
+ * @param flags Flags used to determine the behavior of the adapter,
+ * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
public ResourceCursorAdapter(Context context, int layout, Cursor c, int flags) {
super(context, c, flags);
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 1820c3f..dcd58b0 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -824,7 +824,7 @@
invalidate();
}
updateVoiceButton(!hasText);
- updateViewsVisibility(mIconifiedByDefault);
+ updateCloseButton();
if (mOnQueryChangeListener != null) {
mOnQueryChangeListener.onQueryTextChanged(newText.toString());
}
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index d1c2270..497610c 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -66,7 +66,23 @@
String[] mOriginalFrom;
/**
- * Constructor.
+ * Constructor the enables auto-requery.
+ *
+ * @deprecated This option is discouraged, as it results in Cursor queries
+ * being performed on the application's UI thread and thus can cause poor
+ * responsiveness or even Application Not Responding errors. As an alternative,
+ * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
+ */
+ @Deprecated
+ public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+ super(context, layout, c);
+ mTo = to;
+ mOriginalFrom = from;
+ findColumns(from);
+ }
+
+ /**
+ * Standard constructor.
*
* @param context The context where the ListView associated with this
* SimpleListItemFactory is running
@@ -80,9 +96,12 @@
* These should all be TextViews. The first N views in this list
* are given the values of the first N columns in the from
* parameter. Can be null if the cursor is not available yet.
+ * @param flags Flags used to determine the behavior of the adapter,
+ * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
*/
- public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
- super(context, layout, c);
+ public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from,
+ int[] to, int flags) {
+ super(context, layout, c, flags);
mTo = to;
mOriginalFrom = from;
findColumns(from);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9a2cc89..95a7222 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -864,7 +864,6 @@
mInputType = EditorInfo.TYPE_NULL;
mInput = null;
bufferType = BufferType.SPANNABLE;
- setFocusableInTouchMode(true);
// So that selection can be changed using arrow keys and touch is handled.
setMovementMethod(ArrowKeyMovementMethod.getInstance());
} else if (editable) {
@@ -4073,7 +4072,7 @@
*
* Use {@link #setTextIsSelectable(boolean)} or the
* {@link android.R.styleable#TextView_textIsSelectable} XML attribute to make this TextView
- * selectable (the text is not selectable by default).
+ * selectable (text is not selectable by default).
*
* Note that the content of an EditText is always selectable.
*
@@ -4088,10 +4087,9 @@
/**
* Sets whether or not (default) the content of this view is selectable by the user.
*
- * Note that this methods affect the {@link #setFocusableInTouchMode(boolean)},
- * {@link #setFocusable(boolean)}, {@link #setClickable(boolean)} and
- * {@link #setLongClickable(boolean)} states and you may want to restore these if they were
- * customized.
+ * Note that this methods affect the {@link #setFocusable(boolean)},
+ * {@link #setClickable(boolean)} and {@link #setLongClickable(boolean)} states and you may want
+ * to restore these if they were customized.
*
* See {@link #isTextSelectable} for details.
*
@@ -4102,7 +4100,6 @@
mTextIsSelectable = selectable;
- setFocusableInTouchMode(selectable);
setFocusable(selectable);
setClickable(selectable);
setLongClickable(selectable);
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index b9ae526..2c39871 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -1154,7 +1154,7 @@
}
static jboolean connectPanDeviceNative(JNIEnv *env, jobject object, jstring path,
- jstring srcRole, jstring dstRole) {
+ jstring dstRole) {
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
LOGE("connectPanDeviceNative");
@@ -1165,7 +1165,6 @@
if (nat && eventLoopNat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
- const char *src = env->GetStringUTFChars(srcRole, NULL);
const char *dst = env->GetStringUTFChars(dstRole, NULL);
int len = env->GetStringLength(path) + 1;
@@ -1175,12 +1174,10 @@
bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult,
context_path, eventLoopNat, c_path,
DBUS_NETWORK_IFACE, "Connect",
- DBUS_TYPE_STRING, &src,
DBUS_TYPE_STRING, &dst,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
- env->ReleaseStringUTFChars(srcRole, src);
env->ReleaseStringUTFChars(dstRole, dst);
return ret ? JNI_TRUE : JNI_FALSE;
}
@@ -1274,7 +1271,7 @@
{"setBluetoothTetheringNative", "(ZLjava/lang/String;Ljava/lang/String;)Z",
(void *)setBluetoothTetheringNative},
- {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void *)connectPanDeviceNative},
{"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative},
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 92b50c7..981661a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1335,6 +1335,11 @@
android:exported="true">
</activity>
+ <activity android:name="android.content.SyncActivityTooManyDeletes"
+ android:theme="@android:style/Theme.Holo.Dialog"
+ android:label="@string/sync_too_many_deletes">
+ </activity>
+
<activity android:name="com.android.server.ShutdownActivity"
android:permission="android.permission.SHUTDOWN"
android:excludeFromRecents="true">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 98c9270..873f539 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -48,6 +48,7 @@
theme does not set this value, meaning it is based on whether the
window is floating. -->
<attr name="backgroundDimEnabled" format="boolean" />
+
<!-- =========== -->
<!-- Text styles -->
<!-- =========== -->
@@ -261,7 +262,7 @@
<!-- Flag indicating whether this is a translucent window. -->
<attr name="windowIsTranslucent" format="boolean" />
<!-- Flag indicating that this window's background should be the
- user's current wallpaper. -->
+ user's current wallpaper. -->
<attr name="windowShowWallpaper" format="boolean" />
<!-- This Drawable is overlaid over the foreground of the Window's content area, usually
to place a shadow below the title. -->
@@ -4310,7 +4311,7 @@
If not supplied, then no activity will be launched. -->
<attr name="configure" format="string" />
<!-- A preview of what the AppWidget will look like after it's configured.
- If not supplied, the AppWidget's icon will be used. -->
+ If not supplied, the AppWidget's icon will be used. -->
<attr name="previewImage" format="reference" />
<!-- The view id of the AppWidget subview which should be auto-advanced.
by the widget's host. -->
@@ -4421,6 +4422,10 @@
<attr name="smallIcon" format="reference"/>
<!-- A preferences.xml file for authenticator-specific settings. -->
<attr name="accountPreferences" format="reference"/>
+ <!-- Account handles its own token storage and permissions.
+ Default to false
+ -->
+ <attr name="customTokens" format="boolean"/>
</declare-styleable>
<!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3a5b238..7e06c86 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1407,6 +1407,7 @@
<public type="attr" name="fastScrollPreviewBackgroundRight" />
<public type="attr" name="fastScrollTrackDrawable" />
<public type="attr" name="fastScrollOverlayPosition" />
+ <public type="attr" name="customTokens" />
<public type="anim" name="animator_fade_in" />
<public type="anim" name="animator_fade_out" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e48321c..92f3593 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -298,12 +298,12 @@
<string name="shutdown_confirm_question">Would you like to shut down?</string>
<!-- Recent Tasks dialog: title
- TODO: this should move to SystemUI.apk, but the code for the old
+ TODO: this should move to SystemUI.apk, but the code for the old
recent dialog is still in the framework
-->
<string name="recent_tasks_title">Recent</string>
<!-- Recent Tasks dialog: message when there are no recent applications
- TODO: this should move to SystemUI.apk, but the code for the old
+ TODO: this should move to SystemUI.apk, but the code for the old
recent dialog is still in the framework
-->
<string name="no_recent_tasks">No recent applications.</string>
@@ -1363,17 +1363,17 @@
<!-- Title of policy access to limiting the user's password choices -->
<string name="policylab_limitPassword">Set password rules</string>
<!-- Description of policy access to limiting the user's password choices -->
- <string name="policydesc_limitPassword">Control the length and the characters
+ <string name="policydesc_limitPassword">Control the length and the characters
allowed in screen-unlock passwords</string>
<!-- Title of policy access to watch user login attempts -->
<string name="policylab_watchLogin">Monitor screen-unlock attempts</string>
<!-- Description of policy access to watch user login attempts -->
- <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords
- entered when unlocking the screen, and lock the tablet or erase all the tablet\'s
+ <string name="policydesc_watchLogin" product="tablet">Monitor the number of incorrect passwords
+ entered when unlocking the screen, and lock the tablet or erase all the tablet\'s
data if too many incorrect passwords are entered</string>
<!-- Description of policy access to watch user login attempts -->
- <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords
- entered when unlocking the screen, and lock the phone or erase all the phone\'s
+ <string name="policydesc_watchLogin" product="default">Monitor the number of incorrect passwords
+ entered when unlocking the screen, and lock the phone or erase all the phone\'s
data if too many incorrect passwords are entered</string>
<!-- Title of policy access to reset user's password -->
<string name="policylab_resetPassword">Change the screen-unlock password</string>
@@ -1386,10 +1386,10 @@
<!-- Title of policy access to wipe the user's data -->
<string name="policylab_wipeData">Erase all data</string>
<!-- Description of policy access to wipe the user's data -->
- <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning,
+ <string name="policydesc_wipeData" product="tablet">Erase the tablet\'s data without warning,
by performing a factory data reset</string>
<!-- Description of policy access to wipe the user's data -->
- <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning,
+ <string name="policydesc_wipeData" product="default">Erase the phone\'s data without warning,
by performing a factory data reset</string>
<string name="policylab_setGlobalProxy">Set the device global proxy</string>
<!-- Description of policy access to wipe the user's data -->
@@ -1602,7 +1602,7 @@
<string name="relationTypeSister">Sister</string>
<!-- Spouse relationship type [CHAR LIMIT=20] -->
<string name="relationTypeSpouse">Spouse</string>
-
+
<!-- Custom SIP address type -->
<string name="sipAddressTypeCustom">Custom</string>
<!-- Home SIP address type -->
@@ -2640,4 +2640,15 @@
<!-- Network positioning verification No. Button to push to deny sharing of location
information. -->
<string name="gpsVerifNo">No</string>
+
+ <!-- Error message when the sync tried to delete too many things -->
+ <string name="sync_too_many_deletes">Delete limit exceeded</string>
+ <!-- Dialog message for when there are too many deletes that would take place and we want user confirmation -->
+ <string name="sync_too_many_deletes_desc">There are <xliff:g id="number_of_deleted_items">%1$d</xliff:g> deleted items for <xliff:g id="type_of_sync">%2$s</xliff:g>, account <xliff:g id="account_name">%3$s</xliff:g>. What would you like to do?</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to delete the items -->
+ <string name="sync_really_delete">Delete the items.</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to undo the deletions -->
+ <string name="sync_undo_deletes">Undo the deletes.</string>
+ <!-- Dialog action for when there are too many deletes that would take place and we want user confirmation, and the user wants to do nothing for now -->
+ <string name="sync_do_nothing">Do nothing for now.</string>
</resources>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
index 6717bda..e0a3ee6 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java
@@ -77,7 +77,7 @@
List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks();
boolean found = false;
for (WifiConfiguration c : configList) {
- if (c.networkId == netId) {
+ if (c.networkId == netId && c.SSID.equals(config.SSID)) {
found = true;
}
}
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
new file mode 100644
index 0000000..ad3ec3d
--- /dev/null
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.http;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import junit.framework.TestCase;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
+import tests.http.SocketPolicy;
+import static tests.http.SocketPolicy.DISCONNECT_AT_END;
+import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END;
+import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
+
+public final class DefaultHttpClientTest extends TestCase {
+
+ private MockWebServer server = new MockWebServer();
+
+ @Override protected void tearDown() throws Exception {
+ server.shutdown();
+ super.tearDown();
+ }
+
+ public void testServerClosesSocket() throws Exception {
+ testServerClosesOutput(DISCONNECT_AT_END);
+ }
+
+ public void testServerShutdownInput() throws Exception {
+ testServerClosesOutput(SHUTDOWN_INPUT_AT_END);
+ }
+
+ /**
+ * DefaultHttpClient fails if the server shutdown the output after the
+ * response was sent. http://b/2612240
+ */
+ public void testServerShutdownOutput() throws Exception {
+ testServerClosesOutput(SHUTDOWN_OUTPUT_AT_END);
+ }
+
+ private void testServerClosesOutput(SocketPolicy socketPolicy) throws Exception {
+ server.enqueue(new MockResponse()
+ .setBody("This connection won't pool properly")
+ .setSocketPolicy(socketPolicy));
+ server.enqueue(new MockResponse()
+ .setBody("This comes after a busted connection"));
+ server.play();
+
+ DefaultHttpClient client = new DefaultHttpClient();
+
+ HttpResponse a = client.execute(new HttpGet(server.getUrl("/a").toURI()));
+ assertEquals("This connection won't pool properly", contentToString(a));
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+
+ HttpResponse b = client.execute(new HttpGet(server.getUrl("/b").toURI()));
+ assertEquals("This comes after a busted connection", contentToString(b));
+ // sequence number 0 means the HTTP socket connection was not reused
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+ }
+
+ private String contentToString(HttpResponse response) throws IOException {
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ Reader reader = new InputStreamReader(response.getEntity().getContent());
+ int length;
+ while ((length = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, length);
+ }
+ reader.close();
+ return writer.toString();
+ }
+}
diff --git a/data/keyboards/AVRCP.kl b/data/keyboards/AVRCP.kl
index 4c91ece..736b43c 100644
--- a/data/keyboards/AVRCP.kl
+++ b/data/keyboards/AVRCP.kl
@@ -14,8 +14,8 @@
# Key layout used for Bluetooth AVRCP support.
-key 200 MEDIA_PLAY_PAUSE WAKE
-key 201 MEDIA_PLAY_PAUSE WAKE
+key 200 MEDIA_PLAY WAKE
+key 201 MEDIA_PAUSE WAKE
key 166 MEDIA_STOP WAKE
key 163 MEDIA_NEXT WAKE
key 165 MEDIA_PREVIOUS WAKE
diff --git a/docs/html/guide/developing/debug-tasks.jd b/docs/html/guide/developing/debug-tasks.jd
index f0bf84c..8f40b48 100644
--- a/docs/html/guide/developing/debug-tasks.jd
+++ b/docs/html/guide/developing/debug-tasks.jd
@@ -42,8 +42,8 @@
<dd>Dumps a log of system
messages. The messages include a stack trace when the device throws an error,
as well as {@link android.util.Log} messages you've written from your application. To run
- logcat, execute <code>adb logcat</code> from your Android SDK {@code tools/} directory or,
-from DDMS, select <strong>Device > Run
+ logcat, execute <code>adb logcat</code> from your Android SDK {@code platform-tools/}
+directory or, from DDMS, select <strong>Device > Run
logcat</strong>. When using the <a href="{@docRoot}sdk/eclipse-adt.html">ADT plugin for
Eclipse</a>, you can also view logcat messages by opening the Logcat view, available from
<strong>Window > Show View > Other > Android > Logcat</strong>.
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index 2e2d803..a4cec63 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -104,7 +104,8 @@
</ol>
<p>You can verify that your device is connected by executing <code>adb devices</code> from your
-SDK {@code tools/} directory. If connected, you'll see the device name listed as a "device."</p>
+SDK {@code platform-tools/} directory. If connected, you'll see the device name listed as a
+"device."</p>
<p>If using Eclipse, run or debug as usual. You will be presented
with a <b>Device Chooser</b> dialog that lists the available emulator(s) and connected device(s).
diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd
index 234b18f..d309f47 100644
--- a/docs/html/guide/developing/other-ide.jd
+++ b/docs/html/guide/developing/other-ide.jd
@@ -166,9 +166,10 @@
<p>Once you've created your project, you're ready to begin development.
You can move your project folder wherever you want for development, but keep in mind
that you must use the <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
-(adb) — located in the SDK <code>tools/</code> directory — to send your application
+(adb) — located in the SDK <code>platform-tools/</code> directory — to send your
+application
to the emulator (discussed later). So you need access between your project solution and
-the <code>tools/</code> folder.</p>
+the <code>platform-tools/</code> folder.</p>
<p class="caution"><strong>Caution:</strong> You should refrain from moving the
location of the SDK directory, because this will break the build scripts. (They
@@ -460,7 +461,7 @@
</li>
<li><strong>Install your application</strong>
- <p>From your SDK's <code>tools/</code> directory, install the {@code .apk} on the
+ <p>From your SDK's <code>platform-tools/</code> directory, install the {@code .apk} on the
emulator:
<pre>adb install <em><path_to_your_bin></em>.apk</pre>
<p>Your APK file (signed with either a release or debug key) is in your project {@code bin/}
@@ -507,7 +508,7 @@
Development</a> for more information.</p>
<p>Once your device is set up and connected via USB, navigate to your
-SDK's <code>tools/</code> directory and install the <code>.apk</code> on the device:
+SDK's <code>platform-tools/</code> directory and install the <code>.apk</code> on the device:
<pre>adb -d install <em>path/to/your/app</em>.apk</pre>
<p>The {@code -d} flag specifies that you want to use the attached device (in case you also
have an emulator running).</p>
diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd
index 04eed8e..3c6351e 100644
--- a/docs/html/guide/developing/tools/adb.jd
+++ b/docs/html/guide/developing/tools/adb.jd
@@ -79,6 +79,8 @@
<li>A daemon, which runs as a background process on each emulator or device instance. </li>
</ul>
+<p>You can find the {@code adb} tool in {@code <sdk>/platform-tools/}.</p>
+
<p>When you start an adb client, the client first checks whether there is an adb server process already running. If there isn't, it starts the server process. When the server starts, it binds to local TCP port 5037 and listens for commands sent from adb clients—all adb clients use port 5037 to communicate with the adb server. </p>
<p>The server then sets up connections to all running emulator/device instances. It locates emulator/device instances by scanning odd-numbered ports in the range 5555 to 5585, the range used by emulators/devices. Where the server finds an adb daemon, it sets up a connection to that port. Note that each emulator/device instance acquires a pair of sequential ports — an even-numbered port for console connections and an odd-numbered port for adb connections. For example: </p>
diff --git a/docs/html/guide/developing/tools/draw9patch.jd b/docs/html/guide/developing/tools/draw9patch.jd
index 61da1e0..4d8043b 100644
--- a/docs/html/guide/developing/tools/draw9patch.jd
+++ b/docs/html/guide/developing/tools/draw9patch.jd
@@ -4,8 +4,9 @@
<p>The Draw 9-patch tool allows you to easily create a
{@link android.graphics.NinePatch} graphic using a WYSIWYG editor.</p>
<p>For an introduction to Nine-patch graphics and how they work, please read
-the section on Nine-patch in the
-<a href="{@docRoot}guide/topics/resources/available-resources.html#ninepatch">Nine-patch Images</a> topic.</p>
+the section about Nine-patch in the
+<a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D Graphics</a>
+document.</p>
<img src="{@docRoot}images/draw9patch-norm.png" style="float:right" alt="" height="300" width="341"
/>
diff --git a/docs/html/guide/developing/tools/index.jd b/docs/html/guide/developing/tools/index.jd
index 899c0dc..b3e4625 100644
--- a/docs/html/guide/developing/tools/index.jd
+++ b/docs/html/guide/developing/tools/index.jd
@@ -55,7 +55,8 @@
<dd>The adb tool lets you install your application's .apk files on an
emulator or device and access the emulator or device from a command line.
You can also use it to link a standard debugger to application code running
- on an Android emulator or device.</dd>
+ on an Android emulator or device.
+ <p>This is located in {@code <sdk>/platform-tools/}.</p></dd>
<dt><a href="aapt.html">Android Asset
Packaging Tool</a> (aapt)</dt>
diff --git a/docs/html/guide/topics/graphics/2d-graphics.jd b/docs/html/guide/topics/graphics/2d-graphics.jd
index acb6f9f..5759be5 100644
--- a/docs/html/guide/topics/graphics/2d-graphics.jd
+++ b/docs/html/guide/topics/graphics/2d-graphics.jd
@@ -14,9 +14,9 @@
<li><a href="#drawables-from-xml">Creating from resource XML</a></li>
</ol>
</li>
- <li><a href="#shape-drawable">ShapeDrawable</a></li>
+ <li><a href="#shape-drawable">Shape Drawable</a></li>
<!-- <li><a href="#state-list">StateListDrawable</a></li> -->
- <li><a href="#nine-patch">NinePatchDrawable</a></li>
+ <li><a href="#nine-patch">Nine-patch</a></li>
<li><a href="#tween-animation">Tween Animation</a></li>
<li><a href="#frame-animation">Frame Animation</a></li>
</ol>
@@ -165,7 +165,7 @@
-<h2 id="shape-drawable">ShapeDrawable</h2>
+<h2 id="shape-drawable">Shape Drawable</h2>
<p>When you want to dynamically draw some two-dimensional graphics, a {@link android.graphics.drawable.ShapeDrawable}
object will probably suit your needs. With a ShapeDrawable, you can programmatically draw
@@ -234,6 +234,11 @@
Some properties you might want to adjust include
alpha transparency, color filter, dither, opacity and color.</p>
+<p>You can also define primitive drawable shapes using XML. For more information, see the
+section about Shape Drawables in the <a
+href="{@docRoot}guide/topics/resources/drawable-resource.html#Shape">Drawable Resources</a>
+document.</p>
+
<!-- TODO
<h2 id="state-list">StateListDrawable</h2>
@@ -245,7 +250,7 @@
of the object it's attached to.
-->
-<h2 id="nine-patch">NinePatchDrawable</h2>
+<h2 id="nine-patch">Nine-patch</h2>
<p>A {@link android.graphics.drawable.NinePatchDrawable} graphic is a stretchable bitmap image, which Android
will automatically resize to accommodate the contents of the View in which you have placed it as the background.
@@ -424,8 +429,8 @@
<code>{@link android.view.View#setAnimation(android.view.animation.Animation) View.setAnimation()}</code>.
</p>
-<p>For more information on the XML syntax, available tags and attributes, see the discussion on animation
-in the <a href="{@docRoot}guide/topics/resources/available-resources.html#animation">Available Resources</a>.</p>
+<p>For more information on the XML syntax, available tags and attributes, see <a
+href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
<p class="note"><strong>Note:</strong> Regardless of how your animation may move or resize, the bounds of the
View that holds your animation will not automatically adjust to accommodate it. Even so, the animation will still
@@ -489,4 +494,6 @@
<code>{@link android.app.Activity#onWindowFocusChanged(boolean) onWindowFocusChanged()}</code> method in
your Activity, which will get called when Android brings your window into focus.</p>
+<p>For more information on the XML syntax, available tags and attributes, see <a
+href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 488382e..9de247a 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -446,7 +446,7 @@
</tr>
<td colspan="3"><code>tools/</code></td>
<td>Contains the set of development and profiling tools that are platform-independent, such
-as the emulator, the AVD and SDK Manager, adb, ddms, hierarchyviewer and more. The tools in
+as the emulator, the AVD and SDK Manager, ddms, hierarchyviewer and more. The tools in
this directory may be updated at any time (from the <em>Android SDK Tools</em> component),
independent of platform releases, whereas the tools in {@code platform-tools/} may be updated based
on the latest platform release.</td>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e3bb6eb..b739d83 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -559,7 +559,7 @@
}
/**
- * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
+ * Helper for setFlags(), setting or clearing the FAKE_BOLD_TEXT_FLAG bit
*
* @param fakeBoldText true to set the fakeBoldText bit in the paint's
* flags, false to clear it.
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index 74c9d5d..df5be32 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -71,7 +71,8 @@
bool addStringTag(const char* name, const char* value);
void endFile();
- virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
+ virtual bool scanFile(const char* path, long long lastModified,
+ long long fileSize, bool isDirectory) = 0;
virtual bool handleStringTag(const char* name, const char* value) = 0;
virtual bool setMimeType(const char* mimeType) = 0;
virtual bool addNoMediaFolder(const char* path) = 0;
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 63ec6b2..365fd65 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -407,55 +407,60 @@
private long mFileSize;
private String mWriter;
- public FileCacheEntry beginFile(String path, String mimeType, long lastModified, long fileSize) {
-
- // special case certain file names
- // I use regionMatches() instead of substring() below
- // to avoid memory allocation
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
- // ignore those ._* files created by MacOS
- if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
- return null;
- }
-
- // ignore album art files created by Windows Media Player:
- // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg and AlbumArt_{...}_Small.jpg
- if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
- if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
- path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
- return null;
- }
- int length = path.length() - lastSlash - 1;
- if ((length == 17 && path.regionMatches(true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
- (length == 10 && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
- return null;
- }
- }
- }
-
+ public FileCacheEntry beginFile(String path, String mimeType, long lastModified,
+ long fileSize, boolean isDirectory) {
mMimeType = mimeType;
mFileType = 0;
mFileSize = fileSize;
- // try mimeType first, if it is specified
- if (mimeType != null) {
- mFileType = MediaFile.getFileTypeForMimeType(mimeType);
- }
+ if (!isDirectory) {
+ // special case certain file names
+ // I use regionMatches() instead of substring() below
+ // to avoid memory allocation
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
+ // ignore those ._* files created by MacOS
+ if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
+ return null;
+ }
- // if mimeType was not specified, compute file type based on file extension.
- if (mFileType == 0) {
- MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
- if (mediaFileType != null) {
- mFileType = mediaFileType.fileType;
- if (mMimeType == null) {
- mMimeType = mediaFileType.mimeType;
+ // ignore album art files created by Windows Media Player:
+ // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
+ // and AlbumArt_{...}_Small.jpg
+ if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
+ if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
+ path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
+ return null;
+ }
+ int length = path.length() - lastSlash - 1;
+ if ((length == 17 && path.regionMatches(
+ true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
+ (length == 10
+ && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
+ return null;
+ }
}
}
- }
- if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
- mFileType = getFileTypeFromDrm(path);
+ // try mimeType first, if it is specified
+ if (mimeType != null) {
+ mFileType = MediaFile.getFileTypeForMimeType(mimeType);
+ }
+
+ // if mimeType was not specified, compute file type based on file extension.
+ if (mFileType == 0) {
+ MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
+ if (mediaFileType != null) {
+ mFileType = mediaFileType.fileType;
+ if (mMimeType == null) {
+ mMimeType = mediaFileType.mimeType;
+ }
+ }
+ }
+
+ if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
+ mFileType = getFileTypeFromDrm(path);
+ }
}
String key = path;
@@ -470,7 +475,9 @@
FileCacheEntry entry = mFileCache.get(key);
if (entry == null) {
Uri tableUri;
- if (MediaFile.isVideoFileType(mFileType)) {
+ if (isDirectory) {
+ tableUri = mFilesUri;
+ } else if (MediaFile.isVideoFileType(mFileType)) {
tableUri = mVideoUri;
} else if (MediaFile.isImageFileType(mFileType)) {
tableUri = mImagesUri;
@@ -479,7 +486,8 @@
} else {
tableUri = mFilesUri;
}
- entry = new FileCacheEntry(tableUri, 0, path, 0, 0);
+ entry = new FileCacheEntry(tableUri, 0, path, 0,
+ (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
mFileCache.put(key, entry);
}
entry.mSeenInFileSystem = true;
@@ -514,22 +522,19 @@
return entry;
}
- public void scanFile(String path, long lastModified, long fileSize) {
+ public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory) {
// This is the callback funtion from native codes.
// Log.v(TAG, "scanFile: "+path);
- doScanFile(path, null, lastModified, fileSize, false);
- }
-
- public void scanFile(String path, String mimeType, long lastModified, long fileSize) {
- doScanFile(path, mimeType, lastModified, fileSize, false);
+ doScanFile(path, null, lastModified, fileSize, isDirectory, false);
}
public Uri doScanFile(String path, String mimeType, long lastModified,
- long fileSize, boolean scanAlways) {
+ long fileSize, boolean isDirectory, boolean scanAlways) {
Uri result = null;
// long t1 = System.currentTimeMillis();
try {
- FileCacheEntry entry = beginFile(path, mimeType, lastModified, fileSize);
+ FileCacheEntry entry = beginFile(path, mimeType, lastModified,
+ fileSize, isDirectory);
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
String lowpath = path.toLowerCase();
@@ -775,7 +780,11 @@
values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
}
if (tableUri == mFilesUri) {
- values.put(Files.FileColumns.FORMAT, MediaFile.getFormatCode(entry.mPath, mMimeType));
+ int format = entry.mFormat;
+ if (format == 0) {
+ format = MediaFile.getFormatCode(entry.mPath, mMimeType);
+ }
+ values.put(Files.FileColumns.FORMAT, format);
}
// new file, insert it
result = mMediaProvider.insert(tableUri, values);
@@ -1060,8 +1069,7 @@
boolean fileMissing = false;
if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
- if (entry.mFormat != MtpConstants.FORMAT_ASSOCIATION &&
- inScanDirectory(path, directories)) {
+ if (inScanDirectory(path, directories)) {
// we didn't see this file in the scan directory.
fileMissing = true;
} else {
@@ -1180,7 +1188,7 @@
long lastModifiedSeconds = file.lastModified() / 1000;
// always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(), true);
+ return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),false, true);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
return null;
@@ -1227,7 +1235,8 @@
long lastModifiedSeconds = file.lastModified() / 1000;
// always scan the file, so we can return the content://media Uri for existing files
- mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(), true);
+ mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(),
+ (format == MtpConstants.FORMAT_ASSOCIATION), true);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
diff --git a/media/java/android/media/MediaScannerClient.java b/media/java/android/media/MediaScannerClient.java
index 258c3b4..ac326ef 100644
--- a/media/java/android/media/MediaScannerClient.java
+++ b/media/java/android/media/MediaScannerClient.java
@@ -21,9 +21,7 @@
*/
public interface MediaScannerClient
{
- public void scanFile(String path, long lastModified, long fileSize);
-
- public void scanFile(String path, String mimeType, long lastModified, long fileSize);
+ public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory);
public void addNoMediaFolder(String path);
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index fd0b233..a5176fa 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -62,7 +62,7 @@
}
else {
mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile",
- "(Ljava/lang/String;JJ)V");
+ "(Ljava/lang/String;JJZ)V");
mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag",
"(Ljava/lang/String;Ljava/lang/String;)V");
mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType",
@@ -78,12 +78,14 @@
}
// returns true if it succeeded, false if an exception occured in the Java code
- virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
+ virtual bool scanFile(const char* path, long long lastModified,
+ long long fileSize, bool isDirectory)
{
jstring pathStr;
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
- mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
+ mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
+ fileSize, isDirectory);
mEnv->DeleteLocalRef(pathStr);
return (!mEnv->ExceptionCheck());
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp
index c31b622..5ec573e 100644
--- a/media/libmedia/MediaScanner.cpp
+++ b/media/libmedia/MediaScanner.cpp
@@ -84,6 +84,7 @@
// place to copy file or directory name
char* fileSpot = path + strlen(path);
struct dirent* entry;
+ struct stat statbuf;
// ignore directories that contain a ".nomedia" file
if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
@@ -125,7 +126,6 @@
// If the type is unknown, stat() the file instead.
// This is sometimes necessary when accessing NFS mounted filesystems, but
// could be needed in other cases well.
- struct stat statbuf;
if (stat(path, &statbuf) == 0) {
if (S_ISREG(statbuf.st_mode)) {
type = DT_REG;
@@ -142,8 +142,15 @@
// for example, the Mac ".Trashes" directory
if (name[0] == '.') continue;
+ // report the directory to the client
+ if (stat(path, &statbuf) == 0) {
+ client.scanFile(path, statbuf.st_mtime, 0, true);
+ }
+
+ // and now process its contents
strcat(fileSpot, "/");
- int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client, exceptionCheck, exceptionEnv);
+ int err = doProcessDirectory(path, pathRemaining - nameLength - 1, client,
+ exceptionCheck, exceptionEnv);
if (err) {
// pass exceptions up - ignore other errors
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
@@ -151,11 +158,8 @@
continue;
}
} else {
- struct stat statbuf;
stat(path, &statbuf);
- if (statbuf.st_size > 0) {
- client.scanFile(path, statbuf.st_mtime, statbuf.st_size);
- }
+ client.scanFile(path, statbuf.st_mtime, statbuf.st_size, false);
if (exceptionCheck && exceptionCheck(exceptionEnv)) goto failure;
}
}
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 62567be..30ac404 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -349,7 +349,11 @@
bool firstTime = (mPlaylist == NULL);
mPlaylist = fetchPlaylist(url.c_str());
- CHECK(mPlaylist != NULL);
+ if (mPlaylist == NULL) {
+ LOGE("failed to load playlist at url '%s'", url.c_str());
+ mDataSource->queueEOS(ERROR_IO);
+ return;
+ }
if (firstTime) {
Mutex::Autolock autoLock(mLock);
@@ -446,7 +450,11 @@
sp<ABuffer> buffer;
status_t err = fetchFile(uri.c_str(), &buffer);
- CHECK_EQ(err, (status_t)OK);
+ if (err != OK) {
+ LOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
+ mDataSource->queueEOS(err);
+ return;
+ }
CHECK_EQ((status_t)OK,
decryptBuffer(mSeqNumber - firstSeqNumberInPlaylist, buffer));
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml
index ec57de3..0eaf08e 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml
@@ -31,6 +31,7 @@
android:id="@+id/bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:animateLayoutChanges="true"
>
<!-- notification icons & panel access -->
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
index 2272e34..d3fe2e7 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel.xml
@@ -163,7 +163,7 @@
android:orientation="vertical"
>
<ScrollView
- android:id="@+id/notificationScroller"
+ android:id="@+id/notification_scroller"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_weight="1"
@@ -184,6 +184,7 @@
</com.android.systemui.statusbar.tablet.NotificationLinearLayout>
</ScrollView>
<ImageView
+ android:id="@+id/notification_glow"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_panel_bottom_offset"
android:layout_marginLeft="16dp"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index 82c1d17..759c17c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -39,13 +39,15 @@
public class NotificationPanel extends LinearLayout implements StatusBarPanel,
View.OnClickListener {
- static final String TAG = "NotificationPanel";
+ static final String TAG = "Tablet/NotificationPanel";
+ static final boolean DEBUG = false;
boolean mShowing;
View mTitleArea;
View mSettingsButton;
View mNotificationButton;
View mNotificationScroller;
+ View mNotificationGlow;
ViewGroup mContentFrame;
Rect mContentArea;
View mSettingsView;
@@ -85,7 +87,8 @@
mNotificationButton = (ImageView)findViewById(R.id.notification_button);
mNotificationButton.setOnClickListener(this);
- mNotificationScroller = findViewById(R.id.notificationScroller);
+ mNotificationScroller = findViewById(R.id.notification_scroller);
+ mNotificationGlow = findViewById(R.id.notification_glow);
mContentFrame = (ViewGroup)findViewById(R.id.content_frame);
}
@@ -218,10 +221,11 @@
void addSettingsView() {
LayoutInflater infl = LayoutInflater.from(getContext());
mSettingsView = infl.inflate(R.layout.status_bar_settings_view, mContentFrame, false);
- mContentFrame.addView(mSettingsView);
+ mContentFrame.addView(mSettingsView, mContentFrame.indexOfChild(mNotificationGlow));
}
private class Choreographer implements Animator.AnimatorListener {
+ boolean mVisible;
int mBgAlpha;
ValueAnimator mBgAnim;
int mPanelHeight;
@@ -245,14 +249,16 @@
}
void startAnimation(boolean visible) {
- if (mBgAnim == null) {
+ if (DEBUG) Slog.d(TAG, "startAnimation(visible=" + visible + ")");
+ if (mBgAnim != null && mVisible != visible) {
+ mBgAnim.reverse();
+ mPositionAnim.reverse();
+ } else {
createAnimation(visible);
mBgAnim.start();
mPositionAnim.start();
- } else {
- mBgAnim.reverse();
- mPositionAnim.reverse();
}
+ mVisible = visible;
}
void jumpTo(boolean visible) {
@@ -289,22 +295,32 @@
mGlowDrawable.setAlpha((int)(255 * alpha));
if (false) {
- Slog.d(TAG, "mPanelBottom=" + mPanelBottom + "translationY=" + translationY
+ Slog.d(TAG, "mPanelBottom=" + mPanelBottom + " translationY=" + translationY
+ " alpha=" + alpha + " glowY=" + glowY);
}
}
public void setPanelHeight(int h) {
mPanelHeight = h;
- setPanelBottom(mPanelBottom);
+ if (mPanelBottom == 0) {
+ // fully closed, no animation necessary
+ setPanelBottom(0);
+ } else if (mVisible) {
+ if (DEBUG) {
+ Slog.d(TAG, "panelHeight not zero but trying to open; scheduling an anim to open fully");
+ }
+ startAnimation(true);
+ }
}
public void onAnimationCancel(Animator animation) {
- //Slog.d(TAG, "onAnimationCancel mBgAlpha=" + mBgAlpha);
+ if (DEBUG) Slog.d(TAG, "onAnimationCancel mBgAlpha=" + mBgAlpha);
+ // force this to zero so we close the window
+ mBgAlpha = 0;
}
public void onAnimationEnd(Animator animation) {
- //Slog.d(TAG, "onAnimationEnd mBgAlpha=" + mBgAlpha);
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd mBgAlpha=" + mBgAlpha);
if (mBgAlpha == 0) {
setVisibility(View.GONE);
}
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 25662492..3201f8b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -154,6 +154,8 @@
boolean mNotificationsOn = true;
private RecentAppsPanel mRecentsPanel;
+ public Context getContext() { return mContext; }
+
protected void addPanelWindows() {
final Context context = mContext;
@@ -322,7 +324,7 @@
mNotificationPeekTapDuration = vc.getTapTimeout();
mNotificationFlingVelocity = 300; // px/s
- mTicker = new TabletTicker(context);
+ mTicker = new TabletTicker(this);
// The icons
mBatteryController = new BatteryController(mContext);
@@ -598,6 +600,14 @@
handleNotificationError(key, notification, "Couldn't update icon: " + ic);
return;
}
+
+ if (key == mNotificationPeekKey) {
+ // must update the peek window
+ Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
+ peekMsg.arg1 = mNotificationPeekIndex;
+ mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
+ mHandler.sendMessage(peekMsg);
+ }
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
@@ -706,10 +716,17 @@
if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
| StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
mTicker.add(key, n);
+
+ mNotificationArea.setVisibility(View.GONE);
}
}
}
+ // called by TabletTicker when it's done with all queued ticks
+ public void doneTicking() {
+ mNotificationArea.setVisibility(View.VISIBLE);
+ }
+
public void animateExpand() {
mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
@@ -836,6 +853,10 @@
}
}
+ public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
+ return new NotificationClicker(intent, pkg, tag, id);
+ }
+
private class NotificationClicker implements View.OnClickListener {
private PendingIntent mIntent;
private String mPkg;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index b3aed03..32f1e98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -18,7 +18,9 @@
import java.util.Arrays;
+import android.animation.LayoutTransition;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -45,9 +47,14 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.StatusBarIconView;
-public class TabletTicker extends Handler {
+public class TabletTicker
+ extends Handler
+ implements LayoutTransition.TransitionListener {
+
private static final String TAG = "StatusBar.TabletTicker";
+ private static final boolean CLICKABLE_TICKER = true;
+
// 3 is enough to let us see most cases, but not get so far behind that it's too annoying.
private static final int QUEUE_LENGTH = 3;
@@ -66,8 +73,14 @@
private StatusBarNotification[] mQueue = new StatusBarNotification[QUEUE_LENGTH];
private int mQueuePos;
- public TabletTicker(Context context) {
- mContext = context;
+ private TabletStatusBar mBar;
+
+ private LayoutTransition mLayoutTransition;
+ private boolean mWindowShouldClose;
+
+ public TabletTicker(TabletStatusBar bar) {
+ mBar = bar;
+ mContext = bar.getContext();
}
public void add(IBinder key, StatusBarNotification notification) {
@@ -170,11 +183,7 @@
}
// if there's nothing left, close the window
- // TODO: Do this when the animation is done instead
- if (mCurrentView == null && mWindow != null) {
- WindowManagerImpl.getDefault().removeView(mWindow);
- mWindow = null;
- }
+ mWindowShouldClose = (mCurrentView == null && mWindow != null);
}
private void dequeue() {
@@ -208,11 +217,29 @@
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+// lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
+
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.addTransitionListener(this);
+ view.setLayoutTransition(mLayoutTransition);
lp.setTitle("NotificationTicker");
view.setLayoutParams(lp);
return view;
}
+ public void startTransition(LayoutTransition transition, ViewGroup container,
+ View view, int transitionType) {}
+
+ public void endTransition(LayoutTransition transition, ViewGroup container,
+ View view, int transitionType) {
+ if (mWindowShouldClose) {
+ WindowManagerImpl.getDefault().removeView(mWindow);
+ mWindow = null;
+ mWindowShouldClose = false;
+ mBar.doneTicking();
+ }
+ }
+
private View makeTickerView(StatusBarNotification notification) {
final Notification n = notification.notification;
@@ -266,6 +293,17 @@
largeIcon.setImageBitmap(n.largeIcon);
largeIcon.setVisibility(View.VISIBLE);
}
+
+ if (CLICKABLE_TICKER) {
+ PendingIntent contentIntent = notification.notification.contentIntent;
+ if (contentIntent != null) {
+ group.setOnClickListener(mBar.makeClicker(contentIntent,
+ notification.pkg, notification.tag, notification.id));
+ } else {
+ group.setOnClickListener(null);
+ }
+ }
+
return group;
}
}
diff --git a/services/audioflinger/A2dpAudioInterface.cpp b/services/audioflinger/A2dpAudioInterface.cpp
index aee01ab..d926cb1 100644
--- a/services/audioflinger/A2dpAudioInterface.cpp
+++ b/services/audioflinger/A2dpAudioInterface.cpp
@@ -260,6 +260,7 @@
if (pRate) *pRate = lRate;
mDevice = device;
+ mBufferDurationUs = ((bufferSize() * 1000 )/ frameSize() / sampleRate()) * 1000;
return NO_ERROR;
}
@@ -288,6 +289,7 @@
if (mStandby) {
acquire_wake_lock (PARTIAL_WAKE_LOCK, sA2dpWakeLock);
mStandby = false;
+ mLastWriteTime = systemTime();
}
status = init();
@@ -308,6 +310,15 @@
buffer = (char *)buffer + status;
}
+ // if A2DP sink runs abnormally fast, sleep a little so that audioflinger mixer thread
+ // does no spin and starve other threads.
+ // NOTE: It is likely that the A2DP headset is being disconnected
+ nsecs_t now = systemTime();
+ if ((uint32_t)ns2us(now - mLastWriteTime) < (mBufferDurationUs >> 2)) {
+ LOGV("A2DP sink runs too fast");
+ usleep(mBufferDurationUs - (uint32_t)ns2us(now - mLastWriteTime));
+ }
+ mLastWriteTime = now;
return bytes;
}
@@ -316,7 +327,7 @@
standby();
// Simulate audio output timing in case of error
- usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000);
+ usleep(mBufferDurationUs);
return status;
}
diff --git a/services/audioflinger/A2dpAudioInterface.h b/services/audioflinger/A2dpAudioInterface.h
index cef1926..dbe2c6a 100644
--- a/services/audioflinger/A2dpAudioInterface.h
+++ b/services/audioflinger/A2dpAudioInterface.h
@@ -117,6 +117,8 @@
uint32_t mDevice;
bool mClosing;
bool mSuspended;
+ nsecs_t mLastWriteTime;
+ uint32_t mBufferDurationUs;
};
friend class A2dpAudioStreamOut;
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 95200fa..9c56a2a 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -99,6 +99,7 @@
static final int MSG_SHOW_IM_PICKER = 1;
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
+ static final int MSG_SHOW_IM_CONFIG = 4;
static final int MSG_UNBIND_INPUT = 1000;
static final int MSG_BIND_INPUT = 1010;
@@ -120,6 +121,7 @@
// If IME doesn't support the system locale, the default subtype will be the first defined one.
private static final int DEFAULT_SUBTYPE_ID = 0;
+ private static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
@@ -1313,14 +1315,14 @@
}
public void showInputMethodAndSubtypeEnablerFromClient(
- IInputMethodClient client, String topId) {
- // TODO: Handle topId for setting the top position of the list ActivityManagerNative
+ IInputMethodClient client, String inputMethodId) {
synchronized (mMethodMap) {
if (mCurClient == null || client == null
|| mCurClient.client.asBinder() != client.asBinder()) {
Slog.w(TAG, "Ignoring showInputMethodAndSubtypeEnablerFromClient of: " + client);
}
- mHandler.sendEmptyMessage(MSG_SHOW_IM_SUBTYPE_ENABLER);
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
+ MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
}
}
@@ -1428,7 +1430,12 @@
return true;
case MSG_SHOW_IM_SUBTYPE_ENABLER:
- showInputMethodAndSubtypeEnabler();
+ args = (HandlerCaller.SomeArgs)msg.obj;
+ showInputMethodAndSubtypeEnabler((String)args.arg1);
+ return true;
+
+ case MSG_SHOW_IM_CONFIG:
+ showConfigureInputMethods();
return true;
// ---------------------------------------------------------
@@ -1624,11 +1631,22 @@
showInputMethodMenuInternal(true);
}
- private void showInputMethodAndSubtypeEnabler() {
+ private void showInputMethodAndSubtypeEnabler(String inputMethodId) {
Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ if (!TextUtils.isEmpty(inputMethodId)) {
+ intent.putExtra(EXTRA_INPUT_METHOD_ID, inputMethodId);
+ }
+ mContext.startActivity(intent);
+ }
+
+ private void showConfigureInputMethods() {
+ Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
mContext.startActivity(intent);
}
@@ -1763,7 +1781,7 @@
mDialogBuilder.setPositiveButton(com.android.internal.R.string.more_item_label,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- showInputMethodAndSubtypeEnabler();
+ showConfigureInputMethods();
}
});
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index ba7692d..26dd692 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -4958,7 +4958,7 @@
}
Binder.restoreCallingIdentity(ident);
- if (frame.isEmpty()) {
+ if (frame.isEmpty() || maxLayer == 0) {
return null;
}
@@ -4990,6 +4990,12 @@
rawss = Surface.screenshot(dw, dh, 0, maxLayer);
}
+ if (rawss == null) {
+ Log.w(TAG, "Failure taking screenshot for (" + dw + "x" + dh
+ + ") to layer " + maxLayer);
+ return null;
+ }
+
Bitmap bm = Bitmap.createBitmap(sw, sh, rawss.getConfig());
Matrix matrix = new Matrix();
ScreenRotationAnimation.createRotationMatrix(rot, dw, dh, matrix);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7c4790f..825d90b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2123,6 +2123,7 @@
// invert everything, b/c glReadPixel() below will invert the FB
glViewport(0, 0, sw, sh);
+ glScissor(0, 0, sw, sh);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
@@ -2132,6 +2133,7 @@
// redraw the screen entirely...
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
+
const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
@@ -2169,7 +2171,6 @@
result = NO_MEMORY;
}
}
-
glEnable(GL_SCISSOR_TEST);
glViewport(0, 0, hw_w, hw_h);
glMatrixMode(GL_PROJECTION);
diff --git a/test-runner/src/android/test/LoaderTestCase.java b/test-runner/src/android/test/LoaderTestCase.java
index 8be6590..c8564c2 100644
--- a/test-runner/src/android/test/LoaderTestCase.java
+++ b/test-runner/src/android/test/LoaderTestCase.java
@@ -64,7 +64,7 @@
// Shut the loader down
completedLoader.unregisterListener(this);
completedLoader.stopLoading();
- completedLoader.destroy();
+ completedLoader.reset();
// Store the result, unblocking the test thread
queue.add(data);
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index f597934..e650b87 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1112,7 +1112,7 @@
value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName);
if (!TextUtils.isEmpty(value)) {
- config.SSID = removeDoubleQuotes(value);
+ config.SSID = value;
} else {
config.SSID = null;
}