summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java139
-rw-r--r--core/java/android/app/Activity.java42
-rw-r--r--core/java/android/app/ActivityThread.java89
-rw-r--r--core/java/android/app/DownloadManager.java55
-rw-r--r--core/java/android/app/Fragment.java36
-rw-r--r--core/java/android/app/FragmentManager.java19
-rw-r--r--core/java/android/app/LoadedApk.java95
-rw-r--r--core/java/android/app/LoaderManager.java62
-rw-r--r--core/java/android/app/QueuedWork.java10
-rw-r--r--core/java/android/bluetooth/BluetoothAssignedNumbers.java4
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java86
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java86
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl2
-rw-r--r--core/java/android/content/BroadcastReceiver.java312
-rw-r--r--core/java/android/content/ClipData.java70
-rw-r--r--core/java/android/content/SyncManager.java10
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java18
-rw-r--r--core/java/android/nfc/ErrorCodes.java28
-rw-r--r--core/java/android/nfc/NdefTagConnection.java16
-rw-r--r--core/java/android/nfc/NfcAdapter.java62
-rw-r--r--core/java/android/nfc/RawTagConnection.java39
-rw-r--r--core/java/android/os/BatteryManager.java2
-rw-r--r--core/java/android/os/Bundle.java7
-rw-r--r--core/java/android/os/StrictMode.java36
-rw-r--r--core/java/android/preference/PreferenceActivity.java14
-rw-r--r--core/java/android/preference/PreferenceFragment.java2
-rw-r--r--core/java/android/provider/ContactsContract.java41
-rw-r--r--core/java/android/provider/Downloads.java8
-rw-r--r--core/java/android/provider/Settings.java14
-rw-r--r--core/java/android/server/BluetoothA2dpService.java6
-rw-r--r--core/java/android/server/BluetoothEventLoop.java10
-rw-r--r--core/java/android/server/BluetoothService.java77
-rw-r--r--core/java/android/view/View.java9
-rw-r--r--core/java/android/view/ViewGroup.java13
-rw-r--r--core/java/android/view/ViewRoot.java5
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java12
-rw-r--r--core/java/android/webkit/BrowserFrame.java33
-rw-r--r--core/java/android/webkit/CookieManager.java16
-rw-r--r--core/java/android/webkit/CookieSyncManager.java42
-rw-r--r--core/java/android/webkit/WebView.java10
-rw-r--r--core/java/android/webkit/WebViewCore.java19
-rw-r--r--core/java/android/webkit/ZoomManager.java17
-rw-r--r--core/java/android/widget/SlidingDrawer.java2
-rw-r--r--core/java/android/widget/StackView.java67
-rw-r--r--core/java/android/widget/TabHost.java29
-rw-r--r--core/java/android/widget/TabWidget.java14
-rw-r--r--core/java/android/widget/TextView.java55
-rw-r--r--core/java/com/android/internal/nfc/LlcpServiceSocket.java3
-rw-r--r--core/java/com/android/internal/nfc/LlcpSocket.java3
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java13
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java16
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl2
-rw-r--r--core/java/com/android/internal/view/InputMethodAndSubtypeEnabler.java319
53 files changed, 1393 insertions, 803 deletions
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e269c31..ad8c971 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -83,15 +83,61 @@ public class ValueAnimator extends Animator {
*/
private long mSeekTime = -1;
+ // TODO: We access the following ThreadLocal variables often, some of them on every update.
+ // If ThreadLocal access is significantly expensive, we may want to put all of these
+ // fields into a structure sot hat we just access ThreadLocal once to get the reference
+ // to that structure, then access the structure directly for each field.
+
// The static sAnimationHandler processes the internal timing loop on which all animations
// are based
- private static AnimationHandler sAnimationHandler;
+ private static ThreadLocal<AnimationHandler> sAnimationHandler =
+ new ThreadLocal<AnimationHandler>();
+
+ // The per-thread list of all active animations
+ private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations =
+ new ThreadLocal<ArrayList<ValueAnimator>>() {
+ @Override
+ protected ArrayList<ValueAnimator> initialValue() {
+ return new ArrayList<ValueAnimator>();
+ }
+ };
+
+ // The per-thread set of animations to be started on the next animation frame
+ private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations =
+ new ThreadLocal<ArrayList<ValueAnimator>>() {
+ @Override
+ protected ArrayList<ValueAnimator> initialValue() {
+ return new ArrayList<ValueAnimator>();
+ }
+ };
+
+ /**
+ * Internal per-thread collections used to avoid set collisions as animations start and end
+ * while being processed.
+ */
+ private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims =
+ new ThreadLocal<ArrayList<ValueAnimator>>() {
+ @Override
+ protected ArrayList<ValueAnimator> initialValue() {
+ return new ArrayList<ValueAnimator>();
+ }
+ };
- // The static list of all active animations
- private static final ArrayList<ValueAnimator> sAnimations = new ArrayList<ValueAnimator>();
+ private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims =
+ new ThreadLocal<ArrayList<ValueAnimator>>() {
+ @Override
+ protected ArrayList<ValueAnimator> initialValue() {
+ return new ArrayList<ValueAnimator>();
+ }
+ };
- // The set of animations to be started on the next animation frame
- private static final ArrayList<ValueAnimator> sPendingAnimations = new ArrayList<ValueAnimator>();
+ private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims =
+ new ThreadLocal<ArrayList<ValueAnimator>>() {
+ @Override
+ protected ArrayList<ValueAnimator> initialValue() {
+ return new ArrayList<ValueAnimator>();
+ }
+ };
// The time interpolator to be used if none is set on the animation
private static final TimeInterpolator sDefaultInterpolator =
@@ -136,14 +182,6 @@ public class ValueAnimator extends Animator {
private int mPlayingState = STOPPED;
/**
- * Internal collections used to avoid set collisions as animations start and end while being
- * processed.
- */
- private static final ArrayList<ValueAnimator> sEndingAnims = new ArrayList<ValueAnimator>();
- private static final ArrayList<ValueAnimator> sDelayedAnims = new ArrayList<ValueAnimator>();
- private static final ArrayList<ValueAnimator> sReadyAnims = new ArrayList<ValueAnimator>();
-
- /**
* Flag that denotes whether the animation is set up and ready to go. Used to
* set up animation that has not yet been started.
*/
@@ -609,11 +647,14 @@ public class ValueAnimator extends Animator {
@Override
public void handleMessage(Message msg) {
boolean callAgain = true;
+ ArrayList<ValueAnimator> animations = sAnimations.get();
+ ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
switch (msg.what) {
// TODO: should we avoid sending frame message when starting if we
// were already running?
case ANIMATION_START:
- if (sAnimations.size() > 0 || sDelayedAnims.size() > 0) {
+ ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get();
+ if (animations.size() > 0 || delayedAnims.size() > 0) {
callAgain = false;
}
// pendingAnims holds any animations that have requested to be started
@@ -621,10 +662,10 @@ public class ValueAnimator extends Animator {
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until sPendingAnimations
// is empty.
- while (sPendingAnimations.size() > 0) {
+ while (pendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
- (ArrayList<ValueAnimator>) sPendingAnimations.clone();
- sPendingAnimations.clear();
+ (ArrayList<ValueAnimator>) pendingAnimations.clone();
+ pendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
@@ -633,7 +674,7 @@ public class ValueAnimator extends Animator {
anim.mPlayingState == CANCELED) {
anim.startAnimation();
} else {
- sDelayedAnims.add(anim);
+ delayedAnims.add(anim);
}
}
}
@@ -642,45 +683,47 @@ public class ValueAnimator extends Animator {
// currentTime holds the common time for all animations processed
// during this frame
long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ ArrayList<ValueAnimator> readyAnims = sReadyAnims.get();
+ ArrayList<ValueAnimator> endingAnims = sEndingAnims.get();
// First, process animations currently sitting on the delayed queue, adding
// them to the active animations if they are ready
- int numDelayedAnims = sDelayedAnims.size();
+ int numDelayedAnims = delayedAnims.size();
for (int i = 0; i < numDelayedAnims; ++i) {
- ValueAnimator anim = sDelayedAnims.get(i);
+ ValueAnimator anim = delayedAnims.get(i);
if (anim.delayedAnimationFrame(currentTime)) {
- sReadyAnims.add(anim);
+ readyAnims.add(anim);
}
}
- int numReadyAnims = sReadyAnims.size();
+ int numReadyAnims = readyAnims.size();
if (numReadyAnims > 0) {
for (int i = 0; i < numReadyAnims; ++i) {
- ValueAnimator anim = sReadyAnims.get(i);
+ ValueAnimator anim = readyAnims.get(i);
anim.startAnimation();
- sDelayedAnims.remove(anim);
+ delayedAnims.remove(anim);
}
- sReadyAnims.clear();
+ readyAnims.clear();
}
// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
- int numAnims = sAnimations.size();
+ int numAnims = animations.size();
for (int i = 0; i < numAnims; ++i) {
- ValueAnimator anim = sAnimations.get(i);
+ ValueAnimator anim = animations.get(i);
if (anim.animationFrame(currentTime)) {
- sEndingAnims.add(anim);
+ endingAnims.add(anim);
}
}
- if (sEndingAnims.size() > 0) {
- for (int i = 0; i < sEndingAnims.size(); ++i) {
- sEndingAnims.get(i).endAnimation();
+ if (endingAnims.size() > 0) {
+ for (int i = 0; i < endingAnims.size(); ++i) {
+ endingAnims.get(i).endAnimation();
}
- sEndingAnims.clear();
+ endingAnims.clear();
}
// If there are still active or delayed animations, call the handler again
// after the frameDelay
- if (callAgain && (!sAnimations.isEmpty() || !sDelayedAnims.isEmpty())) {
+ if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
(AnimationUtils.currentAnimationTimeMillis() - currentTime)));
}
@@ -935,13 +978,13 @@ public class ValueAnimator extends Animator {
mCurrentIteration = 0;
mPlayingState = STOPPED;
mStartedDelay = false;
- sPendingAnimations.add(this);
- if (sAnimationHandler == null) {
- sAnimationHandler = new AnimationHandler();
+ sPendingAnimations.get().add(this);
+ AnimationHandler animationHandler = sAnimationHandler.get();
+ if (animationHandler == null) {
+ animationHandler = new AnimationHandler();
+ sAnimationHandler.set(animationHandler);
}
- // TODO: does this put too many messages on the queue if the handler
- // is already running?
- sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+ animationHandler.sendEmptyMessage(ANIMATION_START);
}
@Override
@@ -965,14 +1008,16 @@ public class ValueAnimator extends Animator {
@Override
public void end() {
- if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) {
+ if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) {
// Special case if the animation has not yet started; get it ready for ending
mStartedDelay = false;
- sPendingAnimations.add(this);
- if (sAnimationHandler == null) {
- sAnimationHandler = new AnimationHandler();
+ sPendingAnimations.get().add(this);
+ AnimationHandler animationHandler = sAnimationHandler.get();
+ if (animationHandler == null) {
+ animationHandler = new AnimationHandler();
+ sAnimationHandler.set(animationHandler);
}
- sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+ animationHandler.sendEmptyMessage(ANIMATION_START);
}
// Just set the ENDED flag - this causes the animation to end the next time a frame
// is processed.
@@ -1009,7 +1054,7 @@ public class ValueAnimator extends Animator {
* called on the UI thread.
*/
private void endAnimation() {
- sAnimations.remove(this);
+ sAnimations.get().remove(this);
mPlayingState = STOPPED;
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
@@ -1026,7 +1071,7 @@ public class ValueAnimator extends Animator {
*/
private void startAnimation() {
initAnimation();
- sAnimations.add(this);
+ sAnimations.get().add(this);
if (mStartDelay > 0 && mListeners != null) {
// Listeners were already notified in start() if startDelay is 0; this is
// just for delayed animations
@@ -1225,6 +1270,6 @@ public class ValueAnimator extends Animator {
* @hide
*/
public static int getCurrentAnimationsCount() {
- return sAnimations.size();
+ return sAnimations.get().size();
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f08d88d..33f88d8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -645,11 +645,13 @@ public class Activity extends ContextThemeWrapper
Activity mParent;
boolean mCalled;
boolean mCheckedForLoaderManager;
- boolean mStarted;
+ boolean mLoadersStarted;
private boolean mResumed;
private boolean mStopped;
boolean mFinished;
boolean mStartedActivity;
+ /** true if the activity is going through a transient pause */
+ /*package*/ boolean mTemporaryPause = false;
/** true if the activity is being destroyed in order to recreate it with a new configuration */
/*package*/ boolean mChangingConfigurations = false;
/*package*/ int mConfigChangeFlags;
@@ -768,7 +770,7 @@ public class Activity extends ContextThemeWrapper
return mLoaderManager;
}
mCheckedForLoaderManager = true;
- mLoaderManager = getLoaderManager(-1, mStarted, true);
+ mLoaderManager = getLoaderManager(-1, mLoadersStarted, true);
return mLoaderManager;
}
@@ -777,9 +779,13 @@ public class Activity extends ContextThemeWrapper
mAllLoaderManagers = new SparseArray<LoaderManagerImpl>();
}
LoaderManagerImpl lm = mAllLoaderManagers.get(index);
- if (lm == null && create) {
- lm = new LoaderManagerImpl(started);
- mAllLoaderManagers.put(index, lm);
+ if (lm == null) {
+ if (create) {
+ lm = new LoaderManagerImpl(this, started);
+ mAllLoaderManagers.put(index, lm);
+ }
+ } else {
+ lm.updateActivity(this);
}
return lm;
}
@@ -979,13 +985,16 @@ public class Activity extends ContextThemeWrapper
*/
protected void onStart() {
mCalled = true;
- mStarted = true;
- if (mLoaderManager != null) {
- mLoaderManager.doStart();
- } else if (!mCheckedForLoaderManager) {
- mLoaderManager = getLoaderManager(-1, mStarted, false);
+
+ if (!mLoadersStarted) {
+ mLoadersStarted = true;
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ } else if (!mCheckedForLoaderManager) {
+ mLoaderManager = getLoaderManager(-1, mLoadersStarted, false);
+ }
+ mCheckedForLoaderManager = true;
}
- mCheckedForLoaderManager = true;
}
/**
@@ -1219,7 +1228,6 @@ public class Activity extends ContextThemeWrapper
*/
protected void onPause() {
mCalled = true;
- QueuedWork.waitToFinish();
}
/**
@@ -4249,7 +4257,7 @@ public class Activity extends ContextThemeWrapper
}
final void performStart() {
- mFragments.mStateSaved = false;
+ mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
mInstrumentation.callActivityOnStart(this);
@@ -4267,7 +4275,7 @@ public class Activity extends ContextThemeWrapper
}
final void performRestart() {
- mFragments.mStateSaved = false;
+ mFragments.noteStateNotSaved();
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
@@ -4347,8 +4355,8 @@ public class Activity extends ContextThemeWrapper
}
final void performStop() {
- if (mStarted) {
- mStarted = false;
+ if (mLoadersStarted) {
+ mLoadersStarted = false;
if (mLoaderManager != null) {
if (!mChangingConfigurations) {
mLoaderManager.doStop();
@@ -4407,7 +4415,7 @@ public class Activity extends ContextThemeWrapper
if (Config.LOGV) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
- mFragments.mStateSaved = false;
+ mFragments.noteStateNotSaved();
if (who == null) {
onActivityResult(requestCode, resultCode, data);
} else {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f3f7ee7..754295a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -117,12 +117,14 @@ final class RemoteServiceException extends AndroidRuntimeException {
* {@hide}
*/
public final class ActivityThread {
- static final String TAG = "ActivityThread";
+ /** @hide */
+ public static final String TAG = "ActivityThread";
private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565;
private static final boolean DEBUG = false;
static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
static final boolean DEBUG_MESSAGES = false;
- static final boolean DEBUG_BROADCAST = false;
+ /** @hide */
+ public static final boolean DEBUG_BROADCAST = false;
private static final boolean DEBUG_RESULTS = false;
private static final boolean DEBUG_BACKUP = false;
private static final boolean DEBUG_CONFIGURATION = false;
@@ -262,18 +264,20 @@ public final class ActivityThread {
}
}
- private static final class ReceiverData {
+ private static final class ReceiverData extends BroadcastReceiver.PendingResult {
+ public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
+ boolean ordered, boolean sticky, IBinder token) {
+ super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
+ this.intent = intent;
+ }
+
Intent intent;
ActivityInfo info;
- int resultCode;
- String resultData;
- Bundle resultExtras;
- boolean sync;
- boolean resultAbort;
public String toString() {
return "ReceiverData{intent=" + intent + " packageName=" +
- info.packageName + " resultCode=" + resultCode
- + " resultData=" + resultData + " resultExtras=" + resultExtras + "}";
+ info.packageName + " resultCode=" + getResultCode()
+ + " resultData=" + getResultData() + " resultExtras="
+ + getResultExtras(false) + "}";
}
}
@@ -466,15 +470,9 @@ public final class ActivityThread {
public final void scheduleReceiver(Intent intent, ActivityInfo info,
int resultCode, String data, Bundle extras, boolean sync) {
- ReceiverData r = new ReceiverData();
-
- r.intent = intent;
+ ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
+ sync, false, mAppThread.asBinder());
r.info = info;
- r.resultCode = resultCode;
- r.resultData = data;
- r.resultExtras = extras;
- r.sync = sync;
-
queueOrSendMessage(H.RECEIVER, r);
}
@@ -1757,6 +1755,7 @@ public final class ActivityThread {
for (int i=0; i<N; i++) {
Intent intent = intents.get(i);
intent.setExtrasClassLoader(r.activity.getClassLoader());
+ r.activity.mFragments.noteStateNotSaved();
mInstrumentation.callActivityOnNewIntent(r.activity, intent);
}
}
@@ -1767,11 +1766,13 @@ public final class ActivityThread {
if (r != null) {
final boolean resumed = !r.paused;
if (resumed) {
+ r.activity.mTemporaryPause = true;
mInstrumentation.callActivityOnPause(r.activity);
}
deliverNewIntents(r, intents);
if (resumed) {
mInstrumentation.callActivityOnResume(r.activity);
+ r.activity.mTemporaryPause = false;
}
}
}
@@ -1796,18 +1797,12 @@ public final class ActivityThread {
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
data.intent.setExtrasClassLoader(cl);
- if (data.resultExtras != null) {
- data.resultExtras.setClassLoader(cl);
- }
+ data.setExtrasClassLoader(cl);
receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
} catch (Exception e) {
- try {
- if (DEBUG_BROADCAST) Slog.i(TAG,
- "Finishing failed broadcast to " + data.intent.getComponent());
- mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
- data.resultData, data.resultExtras, data.resultAbort);
- } catch (RemoteException ex) {
- }
+ if (DEBUG_BROADCAST) Slog.i(TAG,
+ "Finishing failed broadcast to " + data.intent.getComponent());
+ data.sendFinished(mgr);
throw new RuntimeException(
"Unable to instantiate receiver " + component
+ ": " + e.toString(), e);
@@ -1825,20 +1820,13 @@ public final class ActivityThread {
+ ", dir=" + packageInfo.getAppDir());
ContextImpl context = (ContextImpl)app.getBaseContext();
- receiver.setOrderedHint(true);
- receiver.setResult(data.resultCode, data.resultData,
- data.resultExtras);
- receiver.setOrderedHint(data.sync);
+ receiver.setPendingResult(data);
receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
- try {
- if (DEBUG_BROADCAST) Slog.i(TAG,
- "Finishing failed broadcast to " + data.intent.getComponent());
- mgr.finishReceiver(mAppThread.asBinder(), data.resultCode,
- data.resultData, data.resultExtras, data.resultAbort);
- } catch (RemoteException ex) {
- }
+ if (DEBUG_BROADCAST) Slog.i(TAG,
+ "Finishing failed broadcast to " + data.intent.getComponent());
+ data.sendFinished(mgr);
if (!mInstrumentation.onException(receiver, e)) {
throw new RuntimeException(
"Unable to start receiver " + component
@@ -1846,22 +1834,8 @@ public final class ActivityThread {
}
}
- QueuedWork.waitToFinish();
-
- try {
- if (data.sync) {
- if (DEBUG_BROADCAST) Slog.i(TAG,
- "Finishing ordered broadcast to " + data.intent.getComponent());
- mgr.finishReceiver(
- mAppThread.asBinder(), receiver.getResultCode(),
- receiver.getResultData(), receiver.getResultExtras(false),
- receiver.getAbortBroadcast());
- } else {
- if (DEBUG_BROADCAST) Slog.i(TAG,
- "Finishing broadcast to " + data.intent.getComponent());
- mgr.finishReceiver(mAppThread.asBinder(), 0, null, null, false);
- }
- } catch (RemoteException ex) {
+ if (receiver.getPendingResult() != null) {
+ data.finish();
}
}
@@ -2344,6 +2318,9 @@ public final class ActivityThread {
r.activity.mConfigChangeFlags |= configChanges;
Bundle state = performPauseActivity(token, finished, true);
+ // Make sure any pending writes are now committed.
+ QueuedWork.waitToFinish();
+
// Tell the activity manager we have paused.
try {
ActivityManagerNative.getDefault().activityPaused(token, state);
@@ -2594,6 +2571,7 @@ public final class ActivityThread {
try {
// Now we are idle.
r.activity.mCalled = false;
+ r.activity.mTemporaryPause = true;
mInstrumentation.callActivityOnPause(r.activity);
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
@@ -2614,6 +2592,7 @@ public final class ActivityThread {
deliverResults(r, res.results);
if (resumed) {
mInstrumentation.callActivityOnResume(r.activity);
+ r.activity.mTemporaryPause = false;
}
}
}
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 5654599..570e494 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -26,7 +26,6 @@ import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.provider.BaseColumns;
import android.provider.Downloads;
import android.util.Pair;
@@ -337,13 +336,34 @@ public class DownloadManager {
private List<Pair<String, String>> mRequestHeaders = new ArrayList<Pair<String, String>>();
private CharSequence mTitle;
private CharSequence mDescription;
- private boolean mShowNotification = true;
private String mMimeType;
private boolean mRoamingAllowed = true;
private int mAllowedNetworkTypes = ~0; // default to all network types allowed
private boolean mIsVisibleInDownloadsUi = true;
/**
+ * This download is visible but only shows in the notifications
+ * while it's in progress.
+ */
+ public static final int VISIBILITY_VISIBLE = 0;
+
+ /**
+ * This download is visible and shows in the notifications while
+ * in progress and after completion.
+ */
+ public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
+
+ /**
+ * This download doesn't show in the UI or in the notifications.
+ */
+ public static final int VISIBILITY_HIDDEN = 2;
+
+ /** can take any of the following values: {@link #VISIBILITY_HIDDEN}
+ * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}, {@link #VISIBILITY_VISIBLE}
+ */
+ private int mNotificationVisibility = VISIBILITY_VISIBLE;
+
+ /**
* @param uri the HTTP URI to download.
*/
public Request(Uri uri) {
@@ -475,9 +495,33 @@ public class DownloadManager {
*
* @param show whether the download manager should show a notification for this download.
* @return this object
+ * @deprecated use {@link #setNotificationVisibility(int)}
*/
+ @Deprecated
public Request setShowRunningNotification(boolean show) {
- mShowNotification = show;
+ return (show) ? setNotificationVisibility(VISIBILITY_VISIBLE) :
+ setNotificationVisibility(VISIBILITY_HIDDEN);
+ }
+
+ /**
+ * Control whether a system notification is posted by the download manager while this
+ * download is running or when it is completed.
+ * If enabled, the download manager posts notifications about downloads
+ * through the system {@link android.app.NotificationManager}.
+ * By default, a notification is shown only when the download is in progress.
+ *<p>
+ * It can take the following values: {@link #VISIBILITY_HIDDEN},
+ * {@link #VISIBILITY_VISIBLE},
+ * {@link #VISIBILITY_VISIBLE_NOTIFY_COMPLETED}.
+ *<p>
+ * If set to {@link #VISIBILITY_HIDDEN}, this requires the permission
+ * android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
+ *
+ * @param visibility the visibility setting value
+ * @return this object
+ */
+ public Request setNotificationVisibility(int visibility) {
+ mNotificationVisibility = visibility;
return this;
}
@@ -540,10 +584,7 @@ public class DownloadManager {
putIfNonNull(values, Downloads.Impl.COLUMN_DESCRIPTION, mDescription);
putIfNonNull(values, Downloads.Impl.COLUMN_MIME_TYPE, mMimeType);
- values.put(Downloads.Impl.COLUMN_VISIBILITY,
- mShowNotification ? Downloads.Impl.VISIBILITY_VISIBLE
- : Downloads.Impl.VISIBILITY_HIDDEN);
-
+ values.put(Downloads.Impl.COLUMN_VISIBILITY, mNotificationVisibility);
values.put(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES, mAllowedNetworkTypes);
values.put(Downloads.Impl.COLUMN_ALLOW_ROAMING, mRoamingAllowed);
values.put(Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, mIsVisibleInDownloadsUi);
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 12bf7e5..f27a15e 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -84,6 +84,10 @@ final class FragmentState implements Parcelable {
return mInstance;
}
+ if (mArguments != null) {
+ mArguments.setClassLoader(activity.getClassLoader());
+ }
+
mInstance = Fragment.instantiate(activity, mClassName, mArguments);
if (mSavedFragmentState != null) {
@@ -403,7 +407,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
View mView;
LoaderManagerImpl mLoaderManager;
- boolean mStarted;
+ boolean mLoadersStarted;
boolean mCheckedForLoaderManager;
/**
@@ -728,7 +732,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
return mLoaderManager;
}
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, true);
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, true);
return mLoaderManager;
}
@@ -880,13 +884,16 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
*/
public void onStart() {
mCalled = true;
- mStarted = true;
- if (!mCheckedForLoaderManager) {
- mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false);
- }
- if (mLoaderManager != null) {
- mLoaderManager.doStart();
+
+ if (!mLoadersStarted) {
+ mLoadersStarted = true;
+ if (!mCheckedForLoaderManager) {
+ mCheckedForLoaderManager = true;
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
+ }
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ }
}
}
@@ -971,7 +978,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
// + " mLoaderManager=" + mLoaderManager);
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false);
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doDestroy();
@@ -1182,7 +1189,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
}
if (mLoaderManager != null) {
writer.print(prefix); writer.print("mLoaderManager="); writer.print(mLoaderManager);
- writer.print(" mStarted="); writer.print(mStarted);
+ writer.print(" mLoadersStarted="); writer.print(mLoadersStarted);
writer.print(" mCheckedForLoaderManager=");
writer.println(mCheckedForLoaderManager);
}
@@ -1190,11 +1197,12 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
void performStop() {
onStop();
- if (mStarted) {
- mStarted = false;
+
+ if (mLoadersStarted) {
+ mLoadersStarted = false;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mIndex, mStarted, false);
+ mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, false);
}
if (mLoaderManager != null) {
if (mActivity == null || !mActivity.mChangingConfigurations) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 37e7253..45f9325 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -86,6 +86,15 @@ public interface FragmentManager {
/**
* Start a series of edit operations on the Fragments associated with
* this FragmentManager.
+ *
+ * <p>Note: A fragment transaction can only be created/committed prior
+ * to an activity saving its state. If you try to commit a transaction
+ * after {@link Activity#onSaveInstanceState Activity.onSaveInstanceState()}
+ * (and prior to a following {@link Activity#onStart Activity.onStart}
+ * or {@link Activity#onResume Activity.onResume()}, you will get an error.
+ * This is because the framework takes care of saving your current fragments
+ * in the state, and if changes are made after the state is saved then they
+ * will be lost.</p>
*/
public FragmentTransaction openTransaction();
@@ -271,6 +280,7 @@ final class FragmentManagerImpl implements FragmentManager {
boolean mNeedMenuInvalidate;
boolean mStateSaved;
+ String mNoTransactionsBecause;
// Temporary vars for state save and restore.
Bundle mStateBundle = null;
@@ -843,6 +853,10 @@ final class FragmentManagerImpl implements FragmentManager {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
+ if (mNoTransactionsBecause != null) {
+ throw new IllegalStateException(
+ "Can not perform this action inside of " + mNoTransactionsBecause);
+ }
synchronized (this) {
if (mPendingActions == null) {
mPendingActions = new ArrayList<Runnable>();
@@ -1186,6 +1200,7 @@ final class FragmentManagerImpl implements FragmentManager {
f.mInLayout = false;
f.mAdded = false;
if (fs.mSavedFragmentState != null) {
+ fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
}
@@ -1271,6 +1286,10 @@ final class FragmentManagerImpl implements FragmentManager {
mActivity = activity;
}
+ public void noteStateNotSaved() {
+ mStateSaved = false;
+ }
+
public void dispatchCreate() {
mStateSaved = false;
moveToState(Fragment.CREATED, false);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0644f96..7f24d27 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -660,37 +660,40 @@ final class LoadedApk {
final IntentReceiverLeaked mLocation;
RuntimeException mUnregisterLocation;
- final class Args implements Runnable {
+ final class Args extends BroadcastReceiver.PendingResult implements Runnable {
private Intent mCurIntent;
- private int mCurCode;
- private String mCurData;
- private Bundle mCurMap;
- private boolean mCurOrdered;
- private boolean mCurSticky;
-
+ private final boolean mOrdered;
+
+ public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
+ boolean ordered, boolean sticky) {
+ super(resultCode, resultData, resultExtras,
+ mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
+ ordered, sticky, mIIntentReceiver.asBinder());
+ mCurIntent = intent;
+ mOrdered = ordered;
+ }
+
public void run() {
- BroadcastReceiver receiver = mReceiver;
+ final BroadcastReceiver receiver = mReceiver;
+ final boolean ordered = mOrdered;
+
if (ActivityThread.DEBUG_BROADCAST) {
int seq = mCurIntent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
+ " seq=" + seq + " to " + mReceiver);
Slog.i(ActivityThread.TAG, " mRegistered=" + mRegistered
- + " mCurOrdered=" + mCurOrdered);
+ + " mOrderedHint=" + ordered);
}
- IActivityManager mgr = ActivityManagerNative.getDefault();
- Intent intent = mCurIntent;
+ final IActivityManager mgr = ActivityManagerNative.getDefault();
+ final Intent intent = mCurIntent;
mCurIntent = null;
if (receiver == null) {
- if (mRegistered && mCurOrdered) {
- try {
- if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
- "Finishing null broadcast to " + mReceiver);
- mgr.finishReceiver(mIIntentReceiver,
- mCurCode, mCurData, mCurMap, false);
- } catch (RemoteException ex) {
- }
+ if (mRegistered && ordered) {
+ if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+ "Finishing null broadcast to " + mReceiver);
+ sendFinished(mgr);
}
return;
}
@@ -698,24 +701,14 @@ final class LoadedApk {
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
- if (mCurMap != null) {
- mCurMap.setClassLoader(cl);
- }
- receiver.setOrderedHint(true);
- receiver.setResult(mCurCode, mCurData, mCurMap);
- receiver.clearAbortBroadcast();
- receiver.setOrderedHint(mCurOrdered);
- receiver.setInitialStickyHint(mCurSticky);
+ setExtrasClassLoader(cl);
+ receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
- if (mRegistered && mCurOrdered) {
- try {
- if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
- "Finishing failed broadcast to " + mReceiver);
- mgr.finishReceiver(mIIntentReceiver,
- mCurCode, mCurData, mCurMap, false);
- } catch (RemoteException ex) {
- }
+ if (mRegistered && ordered) {
+ if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+ "Finishing failed broadcast to " + mReceiver);
+ sendFinished(mgr);
}
if (mInstrumentation == null ||
!mInstrumentation.onException(mReceiver, e)) {
@@ -724,17 +717,9 @@ final class LoadedApk {
+ " in " + mReceiver, e);
}
}
- if (mRegistered && mCurOrdered) {
- try {
- if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
- "Finishing broadcast to " + mReceiver);
- mgr.finishReceiver(mIIntentReceiver,
- receiver.getResultCode(),
- receiver.getResultData(),
- receiver.getResultExtras(false),
- receiver.getAbortBroadcast());
- } catch (RemoteException ex) {
- }
+
+ if (receiver.getPendingResult() != null) {
+ finish();
}
}
}
@@ -798,23 +783,13 @@ final class LoadedApk {
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ " to " + mReceiver);
}
- Args args = new Args();
- args.mCurIntent = intent;
- args.mCurCode = resultCode;
- args.mCurData = data;
- args.mCurMap = extras;
- args.mCurOrdered = ordered;
- args.mCurSticky = sticky;
+ Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
- try {
- if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
- "Finishing sync broadcast to " + mReceiver);
- mgr.finishReceiver(mIIntentReceiver, args.mCurCode,
- args.mCurData, args.mCurMap, false);
- } catch (RemoteException ex) {
- }
+ if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+ "Finishing sync broadcast to " + mReceiver);
+ args.sendFinished(mgr);
}
}
}
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 28abcaa..4d4ea9a 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -40,7 +40,12 @@ public interface LoaderManager {
public Loader<D> onCreateLoader(int id, Bundle args);
/**
- * Called when a previously created loader has finished its load.
+ * Called when a previously created loader has finished its load. Note
+ * that normally an application is <em>not</em> allowed to commit fragment
+ * transactions while in this call, since it can happen after an
+ * activity's state is saved. See {@link FragmentManager#openTransaction()
+ * FragmentManager.openTransaction()} for further discussion on this.
+ *
* @param loader The Loader that has finished.
* @param data The data generated by the Loader.
*/
@@ -102,6 +107,7 @@ class LoaderManagerImpl implements LoaderManager {
// previously run loader until the new loader's data is available.
final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>();
+ Activity mActivity;
boolean mStarted;
boolean mRetaining;
boolean mRetainingStarted;
@@ -172,12 +178,12 @@ class LoaderManagerImpl implements LoaderManager {
stop();
}
}
- if (mStarted && mData != null && mCallbacks != null) {
+ 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.
- mCallbacks.onLoadFinished(mLoader, mData);
+ callOnLoadFinished(mLoader, mData);
}
}
}
@@ -219,9 +225,7 @@ class LoaderManagerImpl implements LoaderManager {
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
mData = data;
- if (mCallbacks != null) {
- mCallbacks.onLoadFinished(loader, data);
- }
+ callOnLoadFinished(loader, data);
if (DEBUG) Log.v(TAG, "onLoadFinished returned: " + this);
@@ -236,6 +240,23 @@ class LoaderManagerImpl implements LoaderManager {
}
}
+ void callOnLoadFinished(Loader<Object> loader, Object data) {
+ if (mCallbacks != null) {
+ String lastBecause = null;
+ if (mActivity != null) {
+ lastBecause = mActivity.mFragments.mNoTransactionsBecause;
+ mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
+ }
+ try {
+ mCallbacks.onLoadFinished(loader, data);
+ } finally {
+ if (mActivity != null) {
+ mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ }
+ }
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -252,10 +273,15 @@ class LoaderManagerImpl implements LoaderManager {
}
}
- LoaderManagerImpl(boolean started) {
+ LoaderManagerImpl(Activity activity, boolean started) {
+ mActivity = activity;
mStarted = started;
}
+ void updateActivity(Activity activity) {
+ mActivity = activity;
+ }
+
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
LoaderInfo info = new LoaderInfo(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
@@ -286,7 +312,7 @@ class LoaderManagerImpl implements LoaderManager {
if (info.mData != null && mStarted) {
// If the loader has already generated its data, report it now.
- info.mCallbacks.onLoadFinished(info.mLoader, info.mData);
+ info.callOnLoadFinished(info.mLoader, info.mData);
}
return (Loader<D>)info.mLoader;
@@ -348,7 +374,13 @@ class LoaderManagerImpl implements LoaderManager {
void doStart() {
if (DEBUG) Log.v(TAG, "Starting: " + this);
-
+ if (mStarted) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Called doStart when already started: " + this, e);
+ return;
+ }
+
// 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--) {
@@ -359,6 +391,12 @@ class LoaderManagerImpl implements LoaderManager {
void doStop() {
if (DEBUG) Log.v(TAG, "Stopping: " + this);
+ if (!mStarted) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Called doStop when not started: " + this, e);
+ return;
+ }
for (int i = mLoaders.size()-1; i >= 0; i--) {
mLoaders.valueAt(i).stop();
@@ -368,6 +406,12 @@ class LoaderManagerImpl implements LoaderManager {
void doRetain() {
if (DEBUG) Log.v(TAG, "Retaining: " + this);
+ if (!mStarted) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Called doRetain when not started: " + this, e);
+ return;
+ }
mRetaining = true;
mStarted = false;
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index af6bb1b..6ee4780 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -88,4 +88,14 @@ public class QueuedWork {
toFinish.run();
}
}
+
+ /**
+ * Returns true if there is pending work to be done. Note that the
+ * result is out of data as soon as you receive it, so be careful how you
+ * use it.
+ */
+ public static boolean hasPendingWork() {
+ return !sPendingWorkFinishers.isEmpty();
+ }
+
}
diff --git a/core/java/android/bluetooth/BluetoothAssignedNumbers.java b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
index 55bc814..580e9ff 100644
--- a/core/java/android/bluetooth/BluetoothAssignedNumbers.java
+++ b/core/java/android/bluetooth/BluetoothAssignedNumbers.java
@@ -23,12 +23,10 @@ package android.bluetooth;
* @see <a href="https://www.bluetooth.org/technical/assignednumbers/identifiers.htm">
* The Official Bluetooth SIG Member Website | Company Identifiers</a>
*
- * @hide
*/
public class BluetoothAssignedNumbers {
- //// Bluetooth SIG Company ID values
-
+ // Bluetooth SIG Company ID values
/*
* Ericsson Technology Licensing.
*/
diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
index fd8f930..0c8e4d9 100644
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -80,6 +80,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
public static final int UNPAIR = 100;
public static final int AUTO_CONNECT_PROFILES = 101;
public static final int TRANSITION_TO_STABLE = 102;
+ public static final int CONNECT_OTHER_PROFILES = 103;
private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs
@@ -149,10 +150,6 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
sendMessage(TRANSITION_TO_STABLE);
}
} else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
- if (!getCurrentState().equals(mBondedDevice)) {
- Log.e(TAG, "State is: " + getCurrentState());
- return;
- }
Message msg = new Message();
msg.what = AUTO_CONNECT_PROFILES;
sendMessageDelayed(msg, AUTO_CONNECT_DELAY);
@@ -330,6 +327,27 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
}
}
break;
+ case CONNECT_OTHER_PROFILES:
+ if (isPhoneDocked(mDevice)) {
+ break;
+ }
+ if (message.arg1 == CONNECT_A2DP_OUTGOING) {
+ if (mA2dpService != null &&
+ mA2dpService.getConnectedDevices().size() == 0) {
+ Log.i(TAG, "A2dp:Connect Other Profiles");
+ mA2dpService.connect(mDevice);
+ }
+ } else if (message.arg1 == CONNECT_HFP_OUTGOING) {
+ if (mHeadsetService == null) {
+ deferMessage(message);
+ } else {
+ if (mHeadsetService.getConnectedDevices().size() == 0) {
+ Log.i(TAG, "Headset:Connect Other Profiles");
+ mHeadsetService.connect(mDevice);
+ }
+ }
+ }
+ break;
case TRANSITION_TO_STABLE:
// ignore.
break;
@@ -440,6 +458,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_PBAP_OUTGOING:
case UNPAIR:
case AUTO_CONNECT_PROFILES:
+ case CONNECT_OTHER_PROFILES:
deferMessage(message);
break;
case TRANSITION_TO_STABLE:
@@ -519,6 +538,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_PBAP_OUTGOING:
case UNPAIR:
case AUTO_CONNECT_PROFILES:
+ case CONNECT_OTHER_PROFILES:
deferMessage(message);
break;
case TRANSITION_TO_STABLE:
@@ -628,6 +648,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_PBAP_OUTGOING:
case UNPAIR:
case AUTO_CONNECT_PROFILES:
+ case CONNECT_OTHER_PROFILES:
deferMessage(message);
break;
case TRANSITION_TO_STABLE:
@@ -705,6 +726,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
case DISCONNECT_PBAP_OUTGOING:
case UNPAIR:
case AUTO_CONNECT_PROFILES:
+ case CONNECT_OTHER_PROFILES:
deferMessage(message);
break;
case TRANSITION_TO_STABLE:
@@ -890,6 +912,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
} else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
return mHeadsetService.acceptIncomingConnect(mDevice);
} else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
+ handleConnectionOfOtherProfiles(command);
return mHeadsetService.createIncomingConnect(mDevice);
}
break;
@@ -899,6 +922,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
}
break;
case CONNECT_A2DP_INCOMING:
+ handleConnectionOfOtherProfiles(command);
// ignore, Bluez takes care
return true;
case CONNECT_HID_OUTGOING:
@@ -960,6 +984,60 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
return false;
}
+ private void handleConnectionOfOtherProfiles(int command) {
+ // The white paper recommendations mentions that when there is a
+ // link loss, it is the responsibility of the remote device to connect.
+ // Many connect only 1 profile - and they connect the second profile on
+ // some user action (like play being pressed) and so we need this code.
+ // Auto Connect code only connects to the last connected device - which
+ // is useful in cases like when the phone reboots. But consider the
+ // following case:
+ // User is connected to the car's phone and A2DP profile.
+ // User comes to the desk and places the phone in the dock
+ // (or any speaker or music system or even another headset) and thus
+ // gets connected to the A2DP profile. User goes back to the car.
+ // Ideally the car's system is supposed to send incoming connections
+ // from both Handsfree and A2DP profile. But they don't. The Auto
+ // connect code, will not work here because we only auto connect to the
+ // last connected device for that profile which in this case is the dock.
+ // Now suppose a user is using 2 headsets simultaneously, one for the
+ // phone profile one for the A2DP profile. If this is the use case, we
+ // expect the user to use the preference to turn off the A2DP profile in
+ // the Settings screen for the first headset. Else, after link loss,
+ // there can be an incoming connection from the first headset which
+ // might result in the connection of the A2DP profile (if the second
+ // headset is slower) and thus the A2DP profile on the second headset
+ // will never get connected.
+ //
+ // TODO(): Handle other profiles here.
+ switch (command) {
+ case CONNECT_HFP_INCOMING:
+ // Connect A2DP if there is no incoming connection
+ // If the priority is OFF - don't auto connect.
+ // If the priority is AUTO_CONNECT, auto connect code takes care.
+ if (mA2dpService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) {
+ Message msg = new Message();
+ msg.what = CONNECT_OTHER_PROFILES;
+ msg.arg1 = CONNECT_A2DP_OUTGOING;
+ sendMessageDelayed(msg, AUTO_CONNECT_DELAY);
+ }
+ break;
+ case CONNECT_A2DP_INCOMING:
+ // This is again against spec. HFP incoming connections should be made
+ // before A2DP, so we should not hit this case. But many devices
+ // don't follow this.
+ if (mHeadsetService.getPriority(mDevice) == BluetoothProfile.PRIORITY_ON) {
+ Message msg = new Message();
+ msg.what = CONNECT_OTHER_PROFILES;
+ msg.arg1 = CONNECT_HFP_OUTGOING;
+ sendMessageDelayed(msg, AUTO_CONNECT_DELAY);
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
/*package*/ BluetoothDevice getDevice() {
return mDevice;
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index c64fdbe..c72be6b 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -86,12 +86,33 @@ public final class BluetoothHeadset implements BluetoothProfile {
/**
- * Broadcast Action: Indicates a headset has posted a vendor-specific event.
- * <p>Always contains the extra fields {@link #EXTRA_DEVICE},
- * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD}, and
- * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS}.
+ * Intent used to broadcast that the headset has posted a
+ * vendor-specific event.
+ *
+ * <p>This intent will have 4 extras and 1 category.
+ * {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor specific
+ * command
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT command
+ * type.
+ * Can be one of {@link #AT_CMD_TYPE_READ}, {@link #AT_CMD_TYPE_TEST},
+ * or {@link #AT_CMD_TYPE_SET}, {@link #AT_CMD_TYPE_BASIC},
+ * {@link #AT_CMD_TYPE_ACTION}.
+ *
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command arguments.
+ *
+ * The category is the Company ID of the vendor defining the
+ * vendor-specific command. {@link BluetoothAssignedNumbers}
+ *
+ * For example, for Plantronics specific events
+ * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55
+ *
+ * <p> For example, an AT+XEVENT=foo,3 will get translated into
+ * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT
+ * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET
+ * EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3
+ *
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
- * @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT =
@@ -100,31 +121,68 @@ public final class BluetoothHeadset implements BluetoothProfile {
/**
* A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
* intents that contains the name of the vendor-specific command.
- * @hide
*/
public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD =
"android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD";
/**
* An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
- * intents that contains the Company ID of the vendor defining the vendor-specific
- * command.
- * @see <a href="https://www.bluetooth.org/Technical/AssignedNumbers/identifiers.htm">
- * Bluetooth SIG Assigned Numbers - Company Identifiers</a>
- * @hide
+ * intents that contains the AT command type of the vendor-specific command.
+ */
+ public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE =
+ "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE";
+
+ /**
+ * AT command type READ used with
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+ * For example, AT+VGM?. There are no arguments for this command type.
+ */
+ public static final int AT_CMD_TYPE_READ = 0;
+
+ /**
+ * AT command type TEST used with
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+ * For example, AT+VGM=?. There are no arguments for this command type.
+ */
+ public static final int AT_CMD_TYPE_TEST = 1;
+
+ /**
+ * AT command type SET used with
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+ * For example, AT+VGM=<args>.
+ */
+ public static final int AT_CMD_TYPE_SET = 2;
+
+ /**
+ * AT command type BASIC used with
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+ * For example, ATD. Single character commands and everything following the
+ * character are arguments.
*/
- public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID =
- "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID";
+ public static final int AT_CMD_TYPE_BASIC = 3;
+
+ /**
+ * AT command type ACTION used with
+ * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE}
+ * For example, AT+CHUP. There are no arguments for action commands.
+ */
+ public static final int AT_CMD_TYPE_ACTION = 4;
/**
* A Parcelable String array extra field in
* {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains
* the arguments to the vendor-specific command.
- * @hide
*/
public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS =
"android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS";
+ /**
+ * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT}
+ * for the companyId
+ */
+ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY =
+ "android.bluetooth.headset.intent.category.companyid";
+
/*
* Headset state when SCO audio is connected
* This state can be one of
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index f0252b7..1eb269d 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -92,4 +92,6 @@ interface IBluetooth
List<BluetoothDevice> getConnectedPanDevices();
boolean connectPanDevice(in BluetoothDevice device);
boolean disconnectPanDevice(in BluetoothDevice device);
+
+ void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState);
}
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index b63d026..5939643 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -17,11 +17,14 @@
package android.content;
import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
import android.app.IActivityManager;
+import android.app.QueuedWork;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
/**
* Base class for code that will receive intents sent by sendBroadcast().
@@ -160,6 +163,226 @@ import android.util.Log;
* the containing process active for the entire time of your operation.
*/
public abstract class BroadcastReceiver {
+ private PendingResult mPendingResult;
+ private boolean mDebugUnregister;
+
+ /**
+ * State for a result that is pending for a broadcast receiver. Returned
+ * by {@link BroadcastReceiver#goAsync() goAsync()}
+ * while in {@link BroadcastReceiver#onReceive BroadcastReceiver.onReceive()}.
+ */
+ public static class PendingResult {
+ /** @hide */
+ public static final int TYPE_COMPONENT = 0;
+ /** @hide */
+ public static final int TYPE_REGISTERED = 1;
+ /** @hide */
+ public static final int TYPE_UNREGISTERED = 2;
+
+ final int mType;
+ final boolean mOrderedHint;
+ final boolean mInitialStickyHint;
+ final IBinder mToken;
+
+ int mResultCode;
+ String mResultData;
+ Bundle mResultExtras;
+ boolean mAbortBroadcast;
+ boolean mFinished;
+
+ /** @hide */
+ public PendingResult(int resultCode, String resultData, Bundle resultExtras,
+ int type, boolean ordered, boolean sticky, IBinder token) {
+ mResultCode = resultCode;
+ mResultData = resultData;
+ mResultExtras = resultExtras;
+ mType = type;
+ mOrderedHint = ordered;
+ mInitialStickyHint = sticky;
+ mToken = token;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#setResultCode(int)
+ * BroadcastReceiver.setResultCode(int)} for
+ * asynchronous broadcast handling.
+ */
+ public final void setResultCode(int code) {
+ checkSynchronousHint();
+ mResultCode = code;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#getResultCode()
+ * BroadcastReceiver.getResultCode()} for
+ * asynchronous broadcast handling.
+ */
+ public final int getResultCode() {
+ return mResultCode;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#setResultData(String)
+ * BroadcastReceiver.setResultData(String)} for
+ * asynchronous broadcast handling.
+ */
+ public final void setResultData(String data) {
+ checkSynchronousHint();
+ mResultData = data;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#getResultData()
+ * BroadcastReceiver.getResultData()} for
+ * asynchronous broadcast handling.
+ */
+ public final String getResultData() {
+ return mResultData;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#setResultExtras(Bundle)
+ * BroadcastReceiver.setResultExtras(Bundle)} for
+ * asynchronous broadcast handling.
+ */
+ public final void setResultExtras(Bundle extras) {
+ checkSynchronousHint();
+ mResultExtras = extras;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#getResultExtras(boolean)
+ * BroadcastReceiver.getResultExtras(boolean)} for
+ * asynchronous broadcast handling.
+ */
+ public final Bundle getResultExtras(boolean makeMap) {
+ Bundle e = mResultExtras;
+ if (!makeMap) return e;
+ if (e == null) mResultExtras = e = new Bundle();
+ return e;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#setResult(int, String, Bundle)
+ * BroadcastReceiver.setResult(int, String, Bundle)} for
+ * asynchronous broadcast handling.
+ */
+ public final void setResult(int code, String data, Bundle extras) {
+ checkSynchronousHint();
+ mResultCode = code;
+ mResultData = data;
+ mResultExtras = extras;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#getAbortBroadcast()
+ * BroadcastReceiver.getAbortBroadcast()} for
+ * asynchronous broadcast handling.
+ */
+ public final boolean getAbortBroadcast() {
+ return mAbortBroadcast;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#abortBroadcast()
+ * BroadcastReceiver.abortBroadcast()} for
+ * asynchronous broadcast handling.
+ */
+ public final void abortBroadcast() {
+ checkSynchronousHint();
+ mAbortBroadcast = true;
+ }
+
+ /**
+ * Version of {@link BroadcastReceiver#clearAbortBroadcast()
+ * BroadcastReceiver.clearAbortBroadcast()} for
+ * asynchronous broadcast handling.
+ */
+ public final void clearAbortBroadcast() {
+ mAbortBroadcast = false;
+ }
+
+ /**
+ * Finish the broadcast. The current result will be sent and the
+ * next broadcast will proceed.
+ */
+ public final void finish() {
+ if (mType == TYPE_COMPONENT) {
+ final IActivityManager mgr = ActivityManagerNative.getDefault();
+ if (QueuedWork.hasPendingWork()) {
+ // If this is a broadcast component, we need to make sure any
+ // queued work is complete before telling AM we are done, so
+ // we don't have our process killed before that. We now know
+ // there is pending work; put another piece of work at the end
+ // of the list to finish the broadcast, so we don't block this
+ // thread (which may be the main thread) to have it finished.
+ //
+ // Note that we don't need to use QueuedWork.add() with the
+ // runnable, since we know the AM is waiting for us until the
+ // executor gets to it.
+ QueuedWork.singleThreadExecutor().execute( new Runnable() {
+ @Override public void run() {
+ if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+ "Finishing broadcast after work to component " + mToken);
+ sendFinished(mgr);
+ }
+ });
+ } else {
+ if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+ "Finishing broadcast to component " + mToken);
+ sendFinished(mgr);
+ }
+ } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
+ if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
+ "Finishing broadcast to " + mToken);
+ final IActivityManager mgr = ActivityManagerNative.getDefault();
+ sendFinished(mgr);
+ }
+ }
+
+ /** @hide */
+ public void setExtrasClassLoader(ClassLoader cl) {
+ if (mResultExtras != null) {
+ mResultExtras.setClassLoader(cl);
+ }
+ }
+
+ /** @hide */
+ public void sendFinished(IActivityManager am) {
+ synchronized (this) {
+ if (mFinished) {
+ throw new IllegalStateException("Broadcast already finished");
+ }
+ mFinished = true;
+
+ try {
+ if (mOrderedHint) {
+ am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
+ mAbortBroadcast);
+ } else {
+ // This broadcast was sent to a component; it is not ordered,
+ // but we still need to tell the activity manager we are done.
+ am.finishReceiver(mToken, 0, null, null, false);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+ }
+
+ void checkSynchronousHint() {
+ // Note that we don't assert when receiving the initial sticky value,
+ // since that may have come from an ordered broadcast. We'll catch
+ // them later when the real broadcast happens again.
+ if (mOrderedHint || mInitialStickyHint) {
+ return;
+ }
+ RuntimeException e = new RuntimeException(
+ "BroadcastReceiver trying to return result during a non-ordered broadcast");
+ e.fillInStackTrace();
+ Log.e("BroadcastReceiver", e.getMessage(), e);
+ }
+ }
+
public BroadcastReceiver() {
}
@@ -197,6 +420,26 @@ public abstract class BroadcastReceiver {
public abstract void onReceive(Context context, Intent intent);
/**
+ * This can be called by an application in {@link #onReceive} to allow
+ * it to keep the broadcast active after returning from that function.
+ * This does <em>not</em> change the expectation of being relatively
+ * responsive to the broadcast (finishing it within 10s), but does allow
+ * the implementation to move work related to it over to another thread
+ * to avoid glitching the main UI thread due to disk IO.
+ *
+ * @return Returns a {@link PendingResult} representing the result of
+ * the active broadcast. The BroadcastRecord itself is no longer active;
+ * all data and other interaction must go through {@link PendingResult}
+ * APIs. The {@link PendingResult#finish PendingResult.finish()} method
+ * must be called once processing of the broadcast is done.
+ */
+ public final PendingResult goAsync() {
+ PendingResult res = mPendingResult;
+ mPendingResult = null;
+ return res;
+ }
+
+ /**
* Provide a binder to an already-running service. This method is synchronous
* and will not start the target service if it is not present, so it is safe
* to call from {@link #onReceive}.
@@ -225,9 +468,9 @@ public abstract class BroadcastReceiver {
* {@link android.app.Activity#RESULT_OK} constants, though the
* actual meaning of this value is ultimately up to the broadcaster.
*
- * <p><strong>This method does not work with non-ordered broadcasts such
+ * <p class="note">This method does not work with non-ordered broadcasts such
* as those sent with {@link Context#sendBroadcast(Intent)
- * Context.sendBroadcast}</strong></p>
+ * Context.sendBroadcast}</p>
*
* @param code The new result code.
*
@@ -235,7 +478,7 @@ public abstract class BroadcastReceiver {
*/
public final void setResultCode(int code) {
checkSynchronousHint();
- mResultCode = code;
+ mPendingResult.mResultCode = code;
}
/**
@@ -244,7 +487,7 @@ public abstract class BroadcastReceiver {
* @return int The current result code.
*/
public final int getResultCode() {
- return mResultCode;
+ return mPendingResult != null ? mPendingResult.mResultCode : 0;
}
/**
@@ -264,7 +507,7 @@ public abstract class BroadcastReceiver {
*/
public final void setResultData(String data) {
checkSynchronousHint();
- mResultData = data;
+ mPendingResult.mResultData = data;
}
/**
@@ -274,7 +517,7 @@ public abstract class BroadcastReceiver {
* @return String The current result data; may be null.
*/
public final String getResultData() {
- return mResultData;
+ return mPendingResult != null ? mPendingResult.mResultData : null;
}
/**
@@ -296,7 +539,7 @@ public abstract class BroadcastReceiver {
*/
public final void setResultExtras(Bundle extras) {
checkSynchronousHint();
- mResultExtras = extras;
+ mPendingResult.mResultExtras = extras;
}
/**
@@ -311,9 +554,12 @@ public abstract class BroadcastReceiver {
* @return Map The current extras map.
*/
public final Bundle getResultExtras(boolean makeMap) {
- Bundle e = mResultExtras;
+ if (mPendingResult == null) {
+ return null;
+ }
+ Bundle e = mPendingResult.mResultExtras;
if (!makeMap) return e;
- if (e == null) mResultExtras = e = new Bundle();
+ if (e == null) mPendingResult.mResultExtras = e = new Bundle();
return e;
}
@@ -341,9 +587,9 @@ public abstract class BroadcastReceiver {
*/
public final void setResult(int code, String data, Bundle extras) {
checkSynchronousHint();
- mResultCode = code;
- mResultData = data;
- mResultExtras = extras;
+ mPendingResult.mResultCode = code;
+ mPendingResult.mResultData = data;
+ mPendingResult.mResultExtras = extras;
}
/**
@@ -353,7 +599,7 @@ public abstract class BroadcastReceiver {
* @return True if the broadcast should be aborted.
*/
public final boolean getAbortBroadcast() {
- return mAbortBroadcast;
+ return mPendingResult != null ? mPendingResult.mAbortBroadcast : false;
}
/**
@@ -372,7 +618,7 @@ public abstract class BroadcastReceiver {
*/
public final void abortBroadcast() {
checkSynchronousHint();
- mAbortBroadcast = true;
+ mPendingResult.mAbortBroadcast = true;
}
/**
@@ -380,7 +626,9 @@ public abstract class BroadcastReceiver {
* broadcast.
*/
public final void clearAbortBroadcast() {
- mAbortBroadcast = false;
+ if (mPendingResult != null) {
+ mPendingResult.mAbortBroadcast = false;
+ }
}
/**
@@ -388,7 +636,7 @@ public abstract class BroadcastReceiver {
* broadcast.
*/
public final boolean isOrderedBroadcast() {
- return mOrderedHint;
+ return mPendingResult != null ? mPendingResult.mOrderedHint : false;
}
/**
@@ -398,7 +646,7 @@ public abstract class BroadcastReceiver {
* not directly the result of a broadcast right now.
*/
public final boolean isInitialStickyBroadcast() {
- return mInitialStickyHint;
+ return mPendingResult != null ? mPendingResult.mInitialStickyHint : false;
}
/**
@@ -406,15 +654,21 @@ public abstract class BroadcastReceiver {
* running in ordered mode.
*/
public final void setOrderedHint(boolean isOrdered) {
- mOrderedHint = isOrdered;
+ // Accidentally left in the SDK.
}
/**
- * For internal use, sets the hint about whether this BroadcastReceiver is
- * receiving the initial sticky broadcast value. @hide
+ * For internal use to set the result data that is active. @hide
*/
- public final void setInitialStickyHint(boolean isInitialSticky) {
- mInitialStickyHint = isInitialSticky;
+ public final void setPendingResult(PendingResult result) {
+ mPendingResult = result;
+ }
+
+ /**
+ * For internal use to set the result data that is active. @hide
+ */
+ public final PendingResult getPendingResult() {
+ return mPendingResult;
}
/**
@@ -440,10 +694,14 @@ public abstract class BroadcastReceiver {
}
void checkSynchronousHint() {
+ if (mPendingResult == null) {
+ throw new IllegalStateException("Call while result is not pending");
+ }
+
// Note that we don't assert when receiving the initial sticky value,
// since that may have come from an ordered broadcast. We'll catch
// them later when the real broadcast happens again.
- if (mOrderedHint || mInitialStickyHint) {
+ if (mPendingResult.mOrderedHint || mPendingResult.mInitialStickyHint) {
return;
}
RuntimeException e = new RuntimeException(
@@ -451,13 +709,5 @@ public abstract class BroadcastReceiver {
e.fillInStackTrace();
Log.e("BroadcastReceiver", e.getMessage(), e);
}
-
- private int mResultCode;
- private String mResultData;
- private Bundle mResultExtras;
- private boolean mAbortBroadcast;
- private boolean mDebugUnregister;
- private boolean mOrderedHint;
- private boolean mInitialStickyHint;
}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index a19b132..6f4d098 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -37,8 +37,9 @@ import java.util.ArrayList;
* each of which can hold one or more representations of an item of data.
* For display to the user, it also has a label and iconic representation.</p>
*
- * <p>A ClipData is a sub-class of {@link ClipDescription}, which describes
- * important meta-data about the clip. In particular, its {@link #getMimeType(int)}
+ * <p>A ClipData contains a {@link ClipDescription}, which describes
+ * important meta-data about the clip. In particular, its
+ * {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)}
* must return correct MIME type(s) describing the data in the clip. For help
* in correctly constructing a clip with the correct MIME type, use
* {@link #newPlainText(CharSequence, Bitmap, CharSequence)},
@@ -62,8 +63,8 @@ import java.util.ArrayList;
* <p>If all you want is the textual representation of the clipped data, you
* can use the convenience method {@link Item#coerceToText Item.coerceToText}.
* In this case there is generally no need to worry about the MIME types
- * reported by {@link #getMimeType(int)}, since any clip item an always be
- * converted to a string.
+ * reported by {@link ClipDescription#getMimeType(int) getDescription().getMimeType(int)},
+ * since any clip item an always be converted to a string.
*
* <p>More complicated exchanges will be done through URIs, in particular
* "content:" URIs. A content URI allows the recipient of a ClippedData item
@@ -133,11 +134,16 @@ import java.util.ArrayList;
* into an editor), then {@link Item#coerceToText(Context)} will ask the content
* provider for the clip URI as text and successfully paste the entire note.
*/
-public class ClipData extends ClipDescription {
- static final String[] MIMETYPES_TEXT_PLAIN = new String[] { MIMETYPE_TEXT_PLAIN };
- static final String[] MIMETYPES_TEXT_URILIST = new String[] { MIMETYPE_TEXT_URILIST };
- static final String[] MIMETYPES_TEXT_INTENT = new String[] { MIMETYPE_TEXT_INTENT };
-
+public class ClipData implements Parcelable {
+ static final String[] MIMETYPES_TEXT_PLAIN = new String[] {
+ ClipDescription.MIMETYPE_TEXT_PLAIN };
+ static final String[] MIMETYPES_TEXT_URILIST = new String[] {
+ ClipDescription.MIMETYPE_TEXT_URILIST };
+ static final String[] MIMETYPES_TEXT_INTENT = new String[] {
+ ClipDescription.MIMETYPE_TEXT_INTENT };
+
+ final ClipDescription mClipDescription;
+
final Bitmap mIcon;
final ArrayList<Item> mItems = new ArrayList<Item>();
@@ -320,7 +326,24 @@ public class ClipData extends ClipDescription {
* @param item The contents of the first item in the clip.
*/
public ClipData(CharSequence label, String[] mimeTypes, Bitmap icon, Item item) {
- super(label, mimeTypes);
+ mClipDescription = new ClipDescription(label, mimeTypes);
+ if (item == null) {
+ throw new NullPointerException("item is null");
+ }
+ mIcon = icon;
+ mItems.add(item);
+ }
+
+ /**
+ * Create a new clip.
+ *
+ * @param description The ClipDescription describing the clip contents.
+ * @param icon Bitmap providing the user with an iconing representation of
+ * the clip.
+ * @param item The contents of the first item in the clip.
+ */
+ public ClipData(ClipDescription description, Bitmap icon, Item item) {
+ mClipDescription = description;
if (item == null) {
throw new NullPointerException("item is null");
}
@@ -329,7 +352,8 @@ public class ClipData extends ClipDescription {
}
/**
- * Create a new ClipData holding data of the type {@link #MIMETYPE_TEXT_PLAIN}.
+ * Create a new ClipData holding data of the type
+ * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
*
* @param label User-visible label for the clip data.
* @param icon Iconic representation of the clip data.
@@ -342,7 +366,8 @@ public class ClipData extends ClipDescription {
}
/**
- * Create a new ClipData holding an Intent with MIME type {@link #MIMETYPE_TEXT_INTENT}.
+ * Create a new ClipData holding an Intent with MIME type
+ * {@link ClipDescription#MIMETYPE_TEXT_INTENT}.
*
* @param label User-visible label for the clip data.
* @param icon Iconic representation of the clip data.
@@ -358,7 +383,7 @@ public class ClipData extends ClipDescription {
* Create a new ClipData holding a URI. If the URI is a content: URI,
* this will query the content provider for the MIME type of its data and
* use that as the MIME type. Otherwise, it will use the MIME type
- * {@link #MIMETYPE_TEXT_URILIST}.
+ * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
*
* @param resolver ContentResolver used to get information about the URI.
* @param label User-visible label for the clip data.
@@ -375,7 +400,7 @@ public class ClipData extends ClipDescription {
mimeTypes = resolver.getStreamTypes(uri, "*/*");
if (mimeTypes == null) {
if (realType != null) {
- mimeTypes = new String[] { realType, MIMETYPE_TEXT_URILIST };
+ mimeTypes = new String[] { realType, ClipDescription.MIMETYPE_TEXT_URILIST };
}
} else {
String[] tmp = new String[mimeTypes.length + (realType != null ? 2 : 1)];
@@ -385,7 +410,7 @@ public class ClipData extends ClipDescription {
i++;
}
System.arraycopy(mimeTypes, 0, tmp, i, mimeTypes.length);
- tmp[i + mimeTypes.length] = MIMETYPE_TEXT_URILIST;
+ tmp[i + mimeTypes.length] = ClipDescription.MIMETYPE_TEXT_URILIST;
mimeTypes = tmp;
}
}
@@ -396,7 +421,8 @@ public class ClipData extends ClipDescription {
}
/**
- * Create a new ClipData holding an URI with MIME type {@link #MIMETYPE_TEXT_URILIST}.
+ * Create a new ClipData holding an URI with MIME type
+ * {@link ClipDescription#MIMETYPE_TEXT_URILIST}.
* Unlike {@link #newUri(ContentResolver, CharSequence, Bitmap, Uri)}, nothing
* is inferred about the URI -- if it is a content: URI holding a bitmap,
* the reported type will still be uri-list. Use this with care!
@@ -411,6 +437,14 @@ public class ClipData extends ClipDescription {
return new ClipData(label, MIMETYPES_TEXT_URILIST, icon, item);
}
+ /**
+ * Return the {@link ClipDescription} associated with this data, describing
+ * what it contains.
+ */
+ public ClipDescription getDescription() {
+ return mClipDescription;
+ }
+
public void addItem(Item item) {
if (item == null) {
throw new NullPointerException("item is null");
@@ -437,7 +471,7 @@ public class ClipData extends ClipDescription {
@Override
public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
+ mClipDescription.writeToParcel(dest, flags);
if (mIcon != null) {
dest.writeInt(1);
mIcon.writeToParcel(dest, flags);
@@ -465,7 +499,7 @@ public class ClipData extends ClipDescription {
}
ClipData(Parcel in) {
- super(in);
+ mClipDescription = new ClipDescription(in);
if (in.readInt() != 0) {
mIcon = Bitmap.CREATOR.createFromParcel(in);
} else {
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index c9115c5..34bd386 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -728,10 +728,16 @@ public class SyncManager implements OnAccountsUpdateListener {
newDelayInMs = maxSyncRetryTimeInSeconds * 1000;
}
+ final long backoff = now + newDelayInMs;
+
mSyncStorageEngine.setBackoff(op.account, op.authority,
- now + newDelayInMs, newDelayInMs);
+ backoff, newDelayInMs);
+
+ op.backoff = backoff;
+ op.updateEffectiveRunTime();
+
synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.account, op.authority, now + newDelayInMs);
+ mSyncQueue.onBackoffChanged(op.account, op.authority, backoff);
}
}
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 96e6f22..93f9a71 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -23,14 +23,12 @@ import android.database.CursorWindow;
*/
public abstract class SQLiteClosable {
private int mReferenceCount = 1;
- private Object mLock = new Object(); // STOPSHIP remove this line
protected abstract void onAllReferencesReleased();
protected void onAllReferencesReleasedFromContainer() {}
public void acquireReference() {
- synchronized(mLock) { // STOPSHIP change 'mLock' to 'this'
- checkRefCount();
+ synchronized(this) {
if (mReferenceCount <= 0) {
throw new IllegalStateException(
"attempt to re-open an already-closed object: " + getObjInfo());
@@ -40,8 +38,7 @@ public abstract class SQLiteClosable {
}
public void releaseReference() {
- synchronized(mLock) { // STOPSHIP change 'mLock' to 'this'
- checkRefCount();
+ synchronized(this) {
mReferenceCount--;
if (mReferenceCount == 0) {
onAllReferencesReleased();
@@ -50,8 +47,7 @@ public abstract class SQLiteClosable {
}
public void releaseReferenceFromContainer() {
- synchronized(mLock) { // STOPSHIP change 'mLock' to 'this'
- checkRefCount();
+ synchronized(this) {
mReferenceCount--;
if (mReferenceCount == 0) {
onAllReferencesReleasedFromContainer();
@@ -76,12 +72,4 @@ public abstract class SQLiteClosable {
buff.append(") ");
return buff.toString();
}
-
- // STOPSHIP remove this method before shipping
- private void checkRefCount() {
- if (mReferenceCount > 1000) {
- throw new IllegalStateException("bad refcount: " + mReferenceCount +
- ". file bug against frameworks->database" + getObjInfo());
- }
- }
}
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 5b76d84..69329df 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -33,6 +33,34 @@ public class ErrorCodes {
}
}
+ public static String asString(int code) {
+ switch (code) {
+ case SUCCESS: return "SUCCESS";
+ case ERROR_IO: return "IO";
+ case ERROR_CANCELLED: return "CANCELLED";
+ case ERROR_TIMEOUT: return "TIMEOUT";
+ case ERROR_BUSY: return "BUSY";
+ case ERROR_CONNECT: return "CONNECT/DISCONNECT";
+// case ERROR_DISCONNECT: return "DISCONNECT";
+ case ERROR_READ: return "READ";
+ case ERROR_WRITE: return "WRITE";
+ case ERROR_INVALID_PARAM: return "INVALID_PARAM";
+ case ERROR_INSUFFICIENT_RESOURCES: return "INSUFFICIENT_RESOURCES";
+ case ERROR_SOCKET_CREATION: return "SOCKET_CREATION";
+ case ERROR_SOCKET_NOT_CONNECTED: return "SOCKET_NOT_CONNECTED";
+ case ERROR_BUFFER_TO_SMALL: return "BUFFER_TO_SMALL";
+ case ERROR_SAP_USED: return "SAP_USED";
+ case ERROR_SERVICE_NAME_USED: return "SERVICE_NAME_USED";
+ case ERROR_SOCKET_OPTIONS: return "SOCKET_OPTIONS";
+ case ERROR_NFC_ON: return "NFC_ON";
+ case ERROR_NOT_INITIALIZED: return "NOT_INITIALIZED";
+ case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED";
+ case ERROR_SE_CONNECTED: return "SE_CONNECTED";
+ case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED";
+ default: return "UNKNOWN ERROR";
+ }
+ }
+
public static final int SUCCESS = 0;
public static final int ERROR_IO = -1;
diff --git a/core/java/android/nfc/NdefTagConnection.java b/core/java/android/nfc/NdefTagConnection.java
index 321b0ec..27fa25c 100644
--- a/core/java/android/nfc/NdefTagConnection.java
+++ b/core/java/android/nfc/NdefTagConnection.java
@@ -42,8 +42,8 @@ public class NdefTagConnection extends RawTagConnection {
* Internal constructor, to be used by NfcAdapter
* @hide
*/
- /* package private */ NdefTagConnection(INfcAdapter service, NdefTag tag, String target) throws RemoteException {
- super(service, tag);
+ /* package private */ NdefTagConnection(NfcAdapter adapter, NdefTag tag, String target) throws RemoteException {
+ super(adapter, tag);
String[] targets = tag.getNdefTargets();
int i;
@@ -63,8 +63,8 @@ public class NdefTagConnection extends RawTagConnection {
* Internal constructor, to be used by NfcAdapter
* @hide
*/
- /* package private */ NdefTagConnection(INfcAdapter service, NdefTag tag) throws RemoteException {
- this(service, tag, tag.getNdefTargets()[0]);
+ /* package private */ NdefTagConnection(NfcAdapter adapter, NdefTag tag) throws RemoteException {
+ this(adapter, tag, tag.getNdefTargets()[0]);
}
/**
@@ -97,7 +97,7 @@ public class NdefTagConnection extends RawTagConnection {
msgArray[0] = msg;
return msgArray;
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died");
+ attemptDeadServiceRecovery(e);
return null;
}
}
@@ -134,7 +134,7 @@ public class NdefTagConnection extends RawTagConnection {
throw new IOException();
}
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died");
+ attemptDeadServiceRecovery(e);
}
}
@@ -161,7 +161,7 @@ public class NdefTagConnection extends RawTagConnection {
throw new IOException();
}
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died");
+ attemptDeadServiceRecovery(e);
return false;
}
}
@@ -188,7 +188,7 @@ public class NdefTagConnection extends RawTagConnection {
return result;
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died");
+ attemptDeadServiceRecovery(e);
return NDEF_MODE_UNKNOWN;
}
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 7f4b4a2..a093d12 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -168,7 +168,10 @@ public final class NfcAdapter {
private static boolean sIsInitialized = false;
private static NfcAdapter sAdapter;
- private final INfcAdapter mService;
+ // Final after construction, except for attemptDeadServiceRecovery()
+ // when NFC crashes.
+ // Not locked - we accept a best effort attempt when NFC crashes.
+ /*package*/ INfcAdapter mService;
private NfcAdapter(INfcAdapter service) {
mService = service;
@@ -194,6 +197,16 @@ public final class NfcAdapter {
}
}
+ /** get handle to NFC service interface */
+ private static synchronized INfcAdapter getServiceInterface() {
+ /* get a handle to NFC service */
+ IBinder b = ServiceManager.getService("nfc");
+ if (b == null) {
+ return null;
+ }
+ return INfcAdapter.Stub.asInterface(b);
+ }
+
/**
* Get a handle to the default NFC Adapter on this Android device.
* <p>
@@ -214,18 +227,31 @@ public final class NfcAdapter {
return null;
}
- /* get a handle to NFC service */
- IBinder b = ServiceManager.getService("nfc");
- if (b == null) {
+ INfcAdapter service = getServiceInterface();
+ if (service == null) {
Log.e(TAG, "could not retrieve NFC service");
return null;
}
- sAdapter = new NfcAdapter(INfcAdapter.Stub.asInterface(b));
+ sAdapter = new NfcAdapter(service);
return sAdapter;
}
}
+ /** NFC service dead - attempt best effort recovery */
+ /*package*/ void attemptDeadServiceRecovery(Exception e) {
+ Log.e(TAG, "NFC service dead - attempting to recover", e);
+ INfcAdapter service = getServiceInterface();
+ if (service == null) {
+ Log.e(TAG, "could not retrieve NFC service during service recovery");
+ return;
+ }
+ /* assigning to mService is not thread-safe, but this is best-effort code
+ * and on a well-behaved system should never happen */
+ mService = service;
+ return;
+ }
+
/**
* Return true if this NFC Adapter has any features enabled.
* <p>
@@ -241,7 +267,7 @@ public final class NfcAdapter {
try {
return mService.isEnabled();
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException in isEnabled()", e);
+ attemptDeadServiceRecovery(e);
return false;
}
}
@@ -258,7 +284,7 @@ public final class NfcAdapter {
try {
return mService.enable();
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException in enable()", e);
+ attemptDeadServiceRecovery(e);
return false;
}
}
@@ -277,7 +303,7 @@ public final class NfcAdapter {
try {
return mService.disable();
} catch (RemoteException e) {
- Log.e(TAG, "RemoteException in disable()", e);
+ attemptDeadServiceRecovery(e);
return false;
}
}
@@ -303,7 +329,7 @@ public final class NfcAdapter {
try {
mService.localSet(message);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
}
}
@@ -317,7 +343,7 @@ public final class NfcAdapter {
try {
return mService.localGet();
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
return null;
}
}
@@ -331,9 +357,9 @@ public final class NfcAdapter {
throw new IllegalArgumentException("mock tag cannot be used for connections");
}
try {
- return new RawTagConnection(mService, tag);
+ return new RawTagConnection(this, tag);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
return null;
}
}
@@ -347,9 +373,9 @@ public final class NfcAdapter {
throw new IllegalArgumentException("mock tag cannot be used for connections");
}
try {
- return new RawTagConnection(mService, tag, target);
+ return new RawTagConnection(this, tag, target);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
return null;
}
}
@@ -363,9 +389,9 @@ public final class NfcAdapter {
throw new IllegalArgumentException("mock tag cannot be used for connections");
}
try {
- return new NdefTagConnection(mService, tag);
+ return new NdefTagConnection(this, tag);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
return null;
}
}
@@ -379,9 +405,9 @@ public final class NfcAdapter {
throw new IllegalArgumentException("mock tag cannot be used for connections");
}
try {
- return new NdefTagConnection(mService, tag, target);
+ return new NdefTagConnection(this, tag, target);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
return null;
}
}
diff --git a/core/java/android/nfc/RawTagConnection.java b/core/java/android/nfc/RawTagConnection.java
index 8442893..24072e5 100644
--- a/core/java/android/nfc/RawTagConnection.java
+++ b/core/java/android/nfc/RawTagConnection.java
@@ -35,15 +35,20 @@ import android.util.Log;
*/
public class RawTagConnection {
- /*package*/ final INfcAdapter mService;
- /*package*/ final INfcTag mTagService;
/*package*/ final Tag mTag;
/*package*/ boolean mIsConnected;
/*package*/ String mSelectedTarget;
+ private final NfcAdapter mAdapter;
+
+ // Following fields are final after construction, except for
+ // during attemptDeadServiceRecovery() when NFC crashes.
+ // Not locked - we accept a best effort attempt when NFC crashes.
+ /*package*/ INfcAdapter mService;
+ /*package*/ INfcTag mTagService;
private static final String TAG = "NFC";
- /* package private */ RawTagConnection(INfcAdapter service, Tag tag, String target) throws RemoteException {
+ /*package*/ RawTagConnection(NfcAdapter adapter, Tag tag, String target) throws RemoteException {
String[] targets = tag.getRawTargets();
int i;
@@ -58,14 +63,28 @@ public class RawTagConnection {
throw new IllegalArgumentException();
}
- mService = service;
- mTagService = service.getNfcTagInterface();
+ mAdapter = adapter;
+ mService = mAdapter.mService;
+ mTagService = mService.getNfcTagInterface();
mTag = tag;
mSelectedTarget = target;
}
- /* package private */ RawTagConnection(INfcAdapter service, Tag tag) throws RemoteException {
- this(service, tag, tag.getRawTargets()[0]);
+ /*package*/ RawTagConnection(NfcAdapter adapter, Tag tag) throws RemoteException {
+ this(adapter, tag, tag.getRawTargets()[0]);
+ }
+
+ /** NFC service dead - attempt best effort recovery */
+ /*package*/ void attemptDeadServiceRecovery(Exception e) {
+ mAdapter.attemptDeadServiceRecovery(e);
+ /* assigning to mService is not thread-safe, but this is best-effort code
+ * and on a well-behaved system should never happen */
+ mService = mAdapter.mService;
+ try {
+ mTagService = mService.getNfcTagInterface();
+ } catch (RemoteException e2) {
+ Log.e(TAG, "second RemoteException trying to recover from dead NFC service", e2);
+ }
}
/**
@@ -101,7 +120,7 @@ public class RawTagConnection {
try {
return mTagService.isPresent(mTag.mServiceHandle);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
return false;
}
}
@@ -136,7 +155,7 @@ public class RawTagConnection {
try {
mTagService.close(mTag.mServiceHandle);
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
}
}
@@ -159,7 +178,7 @@ public class RawTagConnection {
}
return response;
} catch (RemoteException e) {
- Log.e(TAG, "NFC service died", e);
+ attemptDeadServiceRecovery(e);
throw new IOException("NFC service died");
}
}
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 247b281..c62715b 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -87,7 +87,7 @@ public class BatteryManager {
/**
* Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
- * Boolean value set to true if an unsupported charger is attached
+ * Int value set to nonzero if an unsupported charger is attached
* to the device.
* {@hide}
*/
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 0ec1c74..8eac7aa 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -181,6 +181,13 @@ public final class Bundle implements Parcelable, Cloneable {
}
/**
+ * Return the ClassLoader currently associated with this Bundle.
+ */
+ public ClassLoader getClassLoader() {
+ return mClassLoader;
+ }
+
+ /**
* Clones the current Bundle. The internal map is cloned, but the keys and
* values to which it refers are copied by reference.
*/
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 9786959..40aceb3 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -173,6 +173,12 @@ public final class StrictMode {
public static final int PENALTY_GATHER = 0x100;
/**
+ * Mask of all the penalty bits.
+ */
+ private static final int PENALTY_MASK =
+ PENALTY_LOG | PENALTY_DIALOG | PENALTY_DEATH | PENALTY_DROPBOX | PENALTY_GATHER;
+
+ /**
* The current VmPolicy in effect.
*/
private static volatile int sVmPolicyMask = 0;
@@ -882,7 +888,7 @@ public final class StrictMode {
}
}
- // The violationMask, passed to ActivityManager, is a
+ // The violationMaskSubset, passed to ActivityManager, is a
// subset of the original StrictMode policy bitmask, with
// only the bit violated and penalty bits to be executed
// by the ActivityManagerService remaining set.
@@ -900,7 +906,35 @@ public final class StrictMode {
if (violationMaskSubset != 0) {
int violationBit = parseViolationFromMessage(info.crashInfo.exceptionMessage);
violationMaskSubset |= violationBit;
+ final int violationMaskSubsetFinal = violationMaskSubset;
final int savedPolicyMask = getThreadPolicyMask();
+
+ final boolean justDropBox = (info.policy & PENALTY_MASK) == PENALTY_DROPBOX;
+ if (justDropBox) {
+ // If all we're going to ask the activity manager
+ // to do is dropbox it (the common case during
+ // platform development), we can avoid doing this
+ // call synchronously which Binder data suggests
+ // isn't always super fast, despite the implementation
+ // in the ActivityManager trying to be mostly async.
+ new Thread("callActivityManagerForStrictModeDropbox") {
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ try {
+ ActivityManagerNative.getDefault().
+ handleApplicationStrictModeViolation(
+ RuntimeInit.getApplicationObject(),
+ violationMaskSubsetFinal,
+ info);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException handling StrictMode violation", e);
+ }
+ }
+ }.start();
+ return;
+ }
+
+ // Normal synchronous call to the ActivityManager.
try {
// First, remove any policy before we call into the Activity Manager,
// otherwise we'll infinite recurse as we try to log policy violations
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index a59b2f8..0c6a237 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -893,13 +893,13 @@ public abstract class PreferenceActivity extends ListActivity implements
}
}
- public void switchToHeaderInner(String fragmentName, Bundle args, boolean next) {
+ private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {
getFragmentManager().popBackStack(BACK_STACK_PREFS, POP_BACK_STACK_INCLUSIVE);
Fragment f = Fragment.instantiate(this, fragmentName, args);
FragmentTransaction transaction = getFragmentManager().openTransaction();
- transaction.setTransition(next ?
- FragmentTransaction.TRANSIT_FRAGMENT_NEXT :
- FragmentTransaction.TRANSIT_FRAGMENT_PREV);
+ transaction.setTransition(direction == 0 ? FragmentTransaction.TRANSIT_NONE
+ : direction > 0 ? FragmentTransaction.TRANSIT_FRAGMENT_NEXT
+ : FragmentTransaction.TRANSIT_FRAGMENT_PREV);
transaction.replace(com.android.internal.R.id.prefs, f);
transaction.commit();
}
@@ -913,7 +913,7 @@ public abstract class PreferenceActivity extends ListActivity implements
*/
public void switchToHeader(String fragmentName, Bundle args) {
setSelectedHeader(null);
- switchToHeaderInner(fragmentName, args, true);
+ switchToHeaderInner(fragmentName, args, 0);
}
/**
@@ -923,8 +923,8 @@ public abstract class PreferenceActivity extends ListActivity implements
* @param header The new header to display.
*/
public void switchToHeader(Header header) {
- switchToHeaderInner(header.fragment, header.fragmentArguments,
- mHeaders.indexOf(header) > mHeaders.indexOf(mCurHeader));
+ int direction = mHeaders.indexOf(header) - mHeaders.indexOf(mCurHeader);
+ switchToHeaderInner(header.fragment, header.fragmentArguments, direction);
setSelectedHeader(header);
}
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 7629c31..33f37f8 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -151,7 +151,7 @@ public abstract class PreferenceFragment extends Fragment implements
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- return inflater.inflate(com.android.internal.R.layout.preference_list_content,
+ return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
container, false);
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 0448ec0..9e6434a 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -5127,6 +5127,47 @@ public final class ContactsContract {
* <P>Type: TEXT</P>
*/
public static final String NAME = DATA;
+
+ /**
+ * Return the string resource that best describes the given
+ * {@link #TYPE}. Will always return a valid resource.
+ */
+ public static final int getTypeLabelResource(int type) {
+ switch (type) {
+ case TYPE_ASSISTANT: return com.android.internal.R.string.relationTypeAssistant;
+ case TYPE_BROTHER: return com.android.internal.R.string.relationTypeBrother;
+ case TYPE_CHILD: return com.android.internal.R.string.relationTypeChild;
+ case TYPE_DOMESTIC_PARTNER:
+ return com.android.internal.R.string.relationTypeDomesticPartner;
+ case TYPE_FATHER: return com.android.internal.R.string.relationTypeFather;
+ case TYPE_FRIEND: return com.android.internal.R.string.relationTypeFriend;
+ case TYPE_MANAGER: return com.android.internal.R.string.relationTypeManager;
+ case TYPE_MOTHER: return com.android.internal.R.string.relationTypeMother;
+ case TYPE_PARENT: return com.android.internal.R.string.relationTypeParent;
+ case TYPE_PARTNER: return com.android.internal.R.string.relationTypePartner;
+ case TYPE_REFERRED_BY:
+ return com.android.internal.R.string.relationTypeReferredBy;
+ case TYPE_RELATIVE: return com.android.internal.R.string.relationTypeRelative;
+ case TYPE_SISTER: return com.android.internal.R.string.relationTypeSister;
+ case TYPE_SPOUSE: return com.android.internal.R.string.relationTypeSpouse;
+ default: return com.android.internal.R.string.orgTypeCustom;
+ }
+ }
+
+ /**
+ * Return a {@link CharSequence} that best describes the given type,
+ * possibly substituting the given {@link #LABEL} value
+ * for {@link #TYPE_CUSTOM}.
+ */
+ public static final CharSequence getTypeLabel(Resources res, int type,
+ CharSequence label) {
+ if (type == TYPE_CUSTOM && !TextUtils.isEmpty(label)) {
+ return label;
+ } else {
+ final int labelRes = getTypeLabelResource(type);
+ return res.getText(labelRes);
+ }
+ }
}
/**
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index c9e3b69..7054888 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -16,6 +16,7 @@
package android.provider;
+import android.app.DownloadManager;
import android.net.Uri;
/**
@@ -621,18 +622,19 @@ public final class Downloads {
* This download is visible but only shows in the notifications
* while it's in progress.
*/
- public static final int VISIBILITY_VISIBLE = 0;
+ public static final int VISIBILITY_VISIBLE = DownloadManager.Request.VISIBILITY_VISIBLE;
/**
* This download is visible and shows in the notifications while
* in progress and after completion.
*/
- public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED = 1;
+ public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED =
+ DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
/**
* This download doesn't show in the UI or in the notifications.
*/
- public static final int VISIBILITY_HIDDEN = 2;
+ public static final int VISIBILITY_HIDDEN = DownloadManager.Request.VISIBILITY_HIDDEN;
/**
* Constants related to HTTP request headers associated with each download.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 62d2ff2..2b79037 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -279,6 +279,20 @@ public final class Settings {
"android.settings.INPUT_METHOD_SETTINGS";
/**
+ * Activity Action: Show enabler activity to enable/disable input methods and subtypes.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_INPUT_METHOD_AND_SUBTYPE_ENABLER =
+ "android.settings.INPUT_METHOD_AND_SUBTYPE_ENABLER";
+
+ /**
* Activity Action: Show settings to manage the user input dictionary.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 61e2305..b5e85a0 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -483,6 +483,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
if (name.equals(PROPERTY_STATE)) {
int state = convertBluezSinkStringtoState(propValues[1]);
+ log("A2DP: onSinkPropertyChanged newState is: " + state + "mPlayingA2dpDevice: " + mPlayingA2dpDevice);
+
if (mAudioDevices.get(device) == null) {
// This is for an incoming connection for a device not known to us.
// We have authorized it and bluez state has changed.
@@ -496,6 +498,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
handleSinkPlayingStateChange(device, BluetoothA2dp.STATE_NOT_PLAYING,
BluetoothA2dp.STATE_PLAYING);
} else {
+ mPlayingA2dpDevice = null;
int prevState = mAudioDevices.get(device);
handleSinkStateChange(device, prevState, state);
}
@@ -510,7 +513,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
mSinkCount--;
} else if (state == BluetoothA2dp.STATE_CONNECTED) {
mSinkCount ++;
- mPlayingA2dpDevice = null;
}
mAudioDevices.put(device, state);
@@ -534,6 +536,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
+
+ mBluetoothService.sendConnectionStateChange(device, state, prevState);
}
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index f47c553..4f56281 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -253,6 +253,7 @@ class BluetoothEventLoop {
// we filled up our cache.
mBluetoothService.getAllProperties();
}
+ log("Property Changed: " + propValues[0] + " : " + propValues[1]);
String name = propValues[0];
if (name.equals("Name")) {
mBluetoothService.setProperty(name, propValues[1]);
@@ -309,6 +310,8 @@ class BluetoothEventLoop {
// Note: bluez only sends this property change when it restarts.
if (propValues[1].equals("true"))
onRestartRequired();
+ } else if (name.equals("DiscoverableTimeout")) {
+ mBluetoothService.setProperty(name, propValues[1]);
}
}
@@ -319,10 +322,9 @@ class BluetoothEventLoop {
Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
return;
}
- if (DBG) {
- log("Device property changed: " + address + " property: "
- + name + " value: " + propValues[1]);
- }
+ log("Device property changed: " + address + " property: "
+ + name + " value: " + propValues[1]);
+
BluetoothDevice device = mAdapter.getRemoteDevice(address);
if (name.equals("Name")) {
mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 660f9ab..65047c0 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -162,6 +162,7 @@ public class BluetoothService extends IBluetooth.Stub {
private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices;
private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
+ private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
private static String mDockAddress;
private String mDockPin;
@@ -411,6 +412,10 @@ public class BluetoothService extends IBluetooth.Stub {
mAdapterProperties.clear();
mServiceRecordToPid.clear();
+ mProfilesConnected = 0;
+ mProfilesConnecting = 0;
+ mProfilesDisconnecting = 0;
+
if (saveSetting) {
persistBluetoothOnSetting(false);
}
@@ -1563,6 +1568,7 @@ public class BluetoothService extends IBluetooth.Stub {
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
+ sendConnectionStateChange(device, state, prevState);
}
/*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
@@ -1790,7 +1796,7 @@ public class BluetoothService extends IBluetooth.Stub {
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
-
+ sendConnectionStateChange(device, state, prevState);
}
/*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
@@ -2731,6 +2737,75 @@ public class BluetoothService extends IBluetooth.Stub {
}
}
+ public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
+ int prevState) {
+ if (updateCountersAndCheckForConnectionStateChange(device, state, prevState)) {
+ state = getAdapterConnectionState(state);
+ prevState = getAdapterConnectionState(prevState);
+ Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, state);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, prevState);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ log("CONNECTION_STATE_CHANGE: " + device + ": " + prevState + "-> " + state);
+ }
+ }
+
+ private int getAdapterConnectionState(int state) {
+ switch (state) {
+ case BluetoothProfile.STATE_CONNECTING:
+ return BluetoothAdapter.STATE_CONNECTING;
+ case BluetoothProfile.STATE_CONNECTED:
+ return BluetoothAdapter.STATE_CONNECTED;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return BluetoothAdapter.STATE_DISCONNECTING;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return BluetoothAdapter.STATE_DISCONNECTED;
+ default:
+ Log.e(TAG, "Error in getAdapterConnectionState");
+ return -1;
+ }
+ }
+
+ private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
+ int state,
+ int prevState) {
+ switch (state) {
+ case BluetoothProfile.STATE_CONNECTING:
+ mProfilesConnecting++;
+ if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
+ if (mProfilesConnected > 0 || mProfilesConnecting > 1) return false;
+
+ break;
+ case BluetoothProfile.STATE_CONNECTED:
+ if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
+ if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
+
+ mProfilesConnected++;
+
+ if (mProfilesConnected > 1) return false;
+ break;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ mProfilesDisconnecting++;
+ mProfilesConnected--;
+
+ if (mProfilesConnected > 0 || mProfilesDisconnecting > 1) return false;
+
+ break;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
+ if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
+
+ if (prevState == BluetoothAdapter.STATE_CONNECTED) {
+ mProfilesConnected--;
+ }
+
+ if (mProfilesConnected > 0 || mProfilesConnecting > 0) return false;
+ break;
+ }
+ return true;
+ }
+
private static void log(String msg) {
Log.d(TAG, msg);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0456463..87e03cf 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7579,6 +7579,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* the auto scaling to true. Doing so, however, will generate a bitmap of a different
* size than the view. This implies that your application must be able to handle this
* size.</p>
+ *
+ * <p>You should avoid calling this method when hardware acceleration is enabled. If
+ * you do not need the drawing cache bitmap, calling this method will increase memory
+ * usage and cause the view to be rendered in software once, thus negatively impacting
+ * performance.</p>
*
* @see #getDrawingCache()
* @see #destroyDrawingCache()
@@ -7699,7 +7704,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
canvas.translate(-mScrollX, -mScrollY);
mPrivateFlags |= DRAWN;
- mPrivateFlags |= DRAWING_CACHE_VALID;
+ if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated) {
+ mPrivateFlags |= DRAWING_CACHE_VALID;
+ }
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7b2703b..b881bdd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1867,12 +1867,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
final int count = mChildrenCount;
final View[] children = mChildren;
+ final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < count; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
child.setDrawingCacheEnabled(true);
- child.buildDrawingCache(true);
+ if (buildCache) {
+ child.buildDrawingCache(true);
+ }
}
}
@@ -1933,6 +1936,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
+ final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < count; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
@@ -1941,7 +1945,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
bindLayoutAnimation(child);
if (cache) {
child.setDrawingCacheEnabled(true);
- child.buildDrawingCache(true);
+ if (buildCache) {
+ child.buildDrawingCache(true);
+ }
}
}
}
@@ -2205,8 +2211,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
if (!canvas.isHardwareAccelerated()) {
cache = child.getDrawingCache(true);
} else {
- // TODO: bring back
- // displayList = child.getDisplayList();
+ displayList = child.getDisplayList();
}
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 2e368b8..06261bb 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -561,6 +561,11 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
scheduleTraversals();
}
}
+
+ void invalidate() {
+ mDirty.set(0, 0, mWidth, mHeight);
+ scheduleTraversals();
+ }
public ViewParent getParent() {
return null;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 8e355d6..7cb6291 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -33,6 +33,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewRoot;
+import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.IInputConnectionWrapper;
@@ -1411,6 +1412,17 @@ public final class InputMethodManager {
}
}
+ public InputMethodSubtype getCurrentInputMethodSubtype() {
+ synchronized (mH) {
+ try {
+ return mService.getCurrentInputMethodSubtype();
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ return null;
+ }
+ }
+ }
+
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 6e588b9..2087664 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -74,8 +74,6 @@ class BrowserFrame extends Handler {
// queue has been cleared,they are ignored.
private boolean mBlockMessages = false;
private int mOrientation = -1;
- private static String sDatabaseDirectory;
- private static String sCacheDirectory;
// Is this frame the main frame?
private boolean mIsMainFrame;
@@ -229,13 +227,6 @@ class BrowserFrame extends Handler {
AssetManager am = context.getAssets();
nativeCreateFrame(w, am, proxy.getBackForwardList());
- if (sDatabaseDirectory == null) {
- sDatabaseDirectory = appContext.getDatabasePath("dummy").getParent();
- }
- if (sCacheDirectory == null) {
- sCacheDirectory = appContext.getCacheDir().getAbsolutePath();
- }
-
if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
}
@@ -658,22 +649,6 @@ class BrowserFrame extends Handler {
}
/**
- * Called by JNI. Gets the application's database directory, excluding the trailing slash.
- * @return String The application's database directory
- */
- private static String getDatabaseDirectory() {
- return sDatabaseDirectory;
- }
-
- /**
- * Called by JNI. Gets the application's cache directory, excluding the trailing slash.
- * @return String The application's cache directory
- */
- private static String getCacheDirectory() {
- return sCacheDirectory;
- }
-
- /**
* Called by JNI.
* Read from an InputStream into a supplied byte[]
* This method catches any exceptions so they don't crash the JVM.
@@ -752,6 +727,14 @@ class BrowserFrame extends Handler {
}
} else if (type == CONTENT) {
try {
+ // Strip off mimetype, for compatibility with ContentLoader.java
+ // If we don't do this, we can fail to load Gmail attachments,
+ // because the URL being loaded doesn't exactly match the URL we
+ // have permission to read.
+ int mimeIndex = url.lastIndexOf('?');
+ if (mimeIndex != -1) {
+ url = url.substring(0, mimeIndex);
+ }
Uri uri = Uri.parse(url);
return mContext.getContentResolver().openInputStream(uri);
} catch (Exception e) {
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 3010178..dcac243 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -273,6 +273,11 @@ public final class CookieManager {
* @param accept TRUE if accept cookie
*/
public synchronized void setAcceptCookie(boolean accept) {
+ if (useChromiumHttpStack()) {
+ nativeSetAcceptCookie(accept);
+ return;
+ }
+
mAcceptCookie = accept;
}
@@ -281,6 +286,10 @@ public final class CookieManager {
* @return TRUE if accept cookie
*/
public synchronized boolean acceptCookie() {
+ if (useChromiumHttpStack()) {
+ return nativeAcceptCookie();
+ }
+
return mAcceptCookie;
}
@@ -418,6 +427,10 @@ public final class CookieManager {
* @return The cookies in the format of NAME=VALUE [; NAME=VALUE]
*/
public String getCookie(String url) {
+ if (useChromiumHttpStack()) {
+ return nativeGetCookie(url);
+ }
+
WebAddress uri;
try {
uri = new WebAddress(url);
@@ -1035,5 +1048,8 @@ public final class CookieManager {
// Native functions
private static native boolean nativeUseChromiumHttpStack();
+ private static native boolean nativeAcceptCookie();
+ private static native String nativeGetCookie(String url);
private static native void nativeRemoveAllCookie();
+ private static native void nativeSetAcceptCookie(boolean accept);
}
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index abe9178..8b76a3b 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -65,6 +65,11 @@ public final class CookieSyncManager extends WebSyncManager {
// time when last update happened
private long mLastUpdate;
+ // Used by the Chromium HTTP stack. Everything else in this class is used only by the Android
+ // Java HTTP stack.
+ private static String sDatabaseDirectory;
+ private static String sCacheDirectory;
+
private CookieSyncManager(Context context) {
super(context, "CookieSyncManager");
}
@@ -77,11 +82,7 @@ public final class CookieSyncManager extends WebSyncManager {
* @return CookieSyncManager
*/
public static synchronized CookieSyncManager getInstance() {
- if (sRef == null) {
- throw new IllegalStateException(
- "CookieSyncManager::createInstance() needs to be called "
- + "before CookieSyncManager::getInstance()");
- }
+ checkInstanceIsCreated();
return sRef;
}
@@ -92,8 +93,11 @@ public final class CookieSyncManager extends WebSyncManager {
*/
public static synchronized CookieSyncManager createInstance(
Context context) {
+ Context appContext = context.getApplicationContext();
if (sRef == null) {
- sRef = new CookieSyncManager(context.getApplicationContext());
+ sRef = new CookieSyncManager(appContext);
+ sDatabaseDirectory = appContext.getDatabasePath("dummy").getParent();
+ sCacheDirectory = appContext.getCacheDir().getAbsolutePath();
}
return sRef;
}
@@ -210,4 +214,30 @@ public final class CookieSyncManager extends WebSyncManager {
}
}
}
+
+ private static void checkInstanceIsCreated() {
+ if (sRef == null) {
+ throw new IllegalStateException(
+ "CookieSyncManager::createInstance() needs to be called "
+ + "before CookieSyncManager::getInstance()");
+ }
+ }
+
+ /**
+ * Called by JNI. Gets the application's database directory, excluding the trailing slash.
+ * @return String The application's database directory
+ */
+ private static synchronized String getDatabaseDirectory() {
+ checkInstanceIsCreated();
+ return sDatabaseDirectory;
+ }
+
+ /**
+ * Called by JNI. Gets the application's cache directory, excluding the trailing slash.
+ * @return String The application's cache directory
+ */
+ private static synchronized String getCacheDirectory() {
+ checkInstanceIsCreated();
+ return sCacheDirectory;
+ }
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c095199..b668340 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2471,7 +2471,7 @@ public class WebView extends AbsoluteLayout
int viewWidth = getViewWidth();
int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
- int newHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
+ int newHeight = Math.round((getViewHeightWithTitle() - getTitleHeight()) * mZoomManager.getInvScale());
/*
* Because the native side may have already done a layout before the
* View system was able to measure us, we have to send a height of 0 to
@@ -6047,7 +6047,7 @@ public class WebView extends AbsoluteLayout
}
private void doMotionUp(int contentX, int contentY) {
- if (mLogEvent && nativeMotionUp(contentX, contentY, mNavSlop)) {
+ if (nativeMotionUp(contentX, contentY, mNavSlop) && mLogEvent) {
EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
}
if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
@@ -6447,7 +6447,7 @@ public class WebView extends AbsoluteLayout
mUserScroll = false;
final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
setBaseLayer(draw.mBaseLayer, draw.mInvalRegion.getBounds());
- final Point viewSize = draw.mViewPoint;
+ final Point viewSize = draw.mViewSize;
WebViewCore.ViewState viewState = draw.mViewState;
boolean isPictureAfterFirstLayout = viewState != null;
if (isPictureAfterFirstLayout) {
@@ -6470,8 +6470,8 @@ public class WebView extends AbsoluteLayout
// received in the fixed dimension.
final boolean updateLayout = viewSize.x == mLastWidthSent
&& viewSize.y == mLastHeightSent;
- recordNewContentSize(draw.mWidthHeight.x,
- draw.mWidthHeight.y, updateLayout);
+ recordNewContentSize(draw.mContentSize.x,
+ draw.mContentSize.y, updateLayout);
if (DebugFlags.WEB_VIEW) {
Rect b = draw.mInvalRegion.getBounds();
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 1c8e2cd..9a873b6 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -260,9 +260,11 @@ final class WebViewCore {
* Given mimeType, check whether it's supported in Android media framework.
* mimeType could be such as "audio/ogg" and "video/mp4".
*/
- /* package */ static boolean supportsMimeType(String mimeType) {
- return MediaFile.getFileTypeForMimeType(mimeType) > 0;
+ /* package */ static boolean isSupportedMediaMimeType(String mimeType) {
+ int fileType = MediaFile.getFileTypeForMimeType(mimeType);
+ return MediaFile.isAudioFileType(fileType) || MediaFile.isVideoFileType(fileType);
}
+
/**
* Add an error message to the client's console.
* @param message The message to add
@@ -1824,12 +1826,13 @@ final class WebViewCore {
DrawData() {
mBaseLayer = 0;
mInvalRegion = new Region();
- mWidthHeight = new Point();
+ mContentSize = new Point();
}
int mBaseLayer;
Region mInvalRegion;
- Point mViewPoint;
- Point mWidthHeight;
+ // view size that was used by webkit during the most recent layout
+ Point mViewSize;
+ Point mContentSize;
int mMinPrefWidth;
// only non-null if it is for the first picture set after the first layout
ViewState mViewState;
@@ -1840,16 +1843,14 @@ final class WebViewCore {
mDrawIsScheduled = false;
DrawData draw = new DrawData();
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
- draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight);
+ draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
if (draw.mBaseLayer == 0) {
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
return;
}
if (mWebView != null) {
- // Send the native view size that was used during the most recent
- // layout.
draw.mFocusSizeChanged = nativeFocusBoundsChanged();
- draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
+ draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
if (mSettings.getUseWideViewPort()) {
draw.mMinPrefWidth = Math.max(
mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 91c0fb2..86dfd1f 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -793,9 +793,9 @@ class ZoomManager {
// bound to match the default scale for mobile sites.
setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth,
Math.max((int) (viewWidth * mInvDefaultScale),
- Math.max(drawData.mMinPrefWidth, drawData.mViewPoint.x))));
+ Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x))));
} else {
- final int contentWidth = drawData.mWidthHeight.x;
+ final int contentWidth = drawData.mContentSize.x;
setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth, contentWidth));
}
}
@@ -826,16 +826,16 @@ class ZoomManager {
assert mWebView.getSettings() != null;
WebViewCore.ViewState viewState = drawData.mViewState;
- final Point viewSize = drawData.mViewPoint;
+ final Point viewSize = drawData.mViewSize;
updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
if (mWebView.getSettings().getUseWideViewPort() &&
mWebView.getSettings().getUseFixedViewport()) {
- final int contentWidth = drawData.mWidthHeight.x;
+ final int contentWidth = drawData.mContentSize.x;
setZoomOverviewWidth(Math.min(WebView.sMaxViewportWidth, contentWidth));
}
if (!mWebView.drawHistory()) {
- final float scale;
+ float scale;
final boolean reflowText;
WebSettings settings = mWebView.getSettings();
@@ -847,12 +847,13 @@ class ZoomManager {
scale = viewState.mViewScale;
reflowText = false;
} else {
+ scale = getZoomOverviewScale();
if (settings.getUseWideViewPort()
- && (settings.getLoadWithOverviewMode() || settings.getUseFixedViewport())) {
+ && settings.getLoadWithOverviewMode()) {
mInitialZoomOverview = true;
- scale = (float) mWebView.getViewWidth() / mZoomOverviewWidth;
} else {
- scale = viewState.mTextWrapScale;
+ scale = Math.max(viewState.mTextWrapScale, scale);
+ mInitialZoomOverview = !exceedsMinScaleIncrement(scale, getZoomOverviewScale());
}
reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale);
}
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index 11d72de..bdeb5c2 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -652,7 +652,7 @@ public class SlidingDrawer extends ViewGroup {
// Try only once... we should really loop but it's not a big deal
// if the draw was cancelled, it will only be temporary anyway
content.getViewTreeObserver().dispatchOnPreDraw();
- content.buildDrawingCache();
+ if (!content.isHardwareAccelerated()) content.buildDrawingCache();
content.setVisibility(View.GONE);
}
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 0e92eeb..e6d5984 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -114,6 +114,7 @@ public class StackView extends AdapterViewAnimator {
private int mTouchSlop;
private int mMaximumVelocity;
private VelocityTracker mVelocityTracker;
+ private boolean mTransitionIsSetup = false;
private static HolographicHelper sHolographicHelper;
private ImageView mHighlight;
@@ -225,6 +226,48 @@ public class StackView extends AdapterViewAnimator {
}
}
+ private void setupStackSlider(View v, int mode) {
+ mStackSlider.setMode(mode);
+ if (v != null) {
+ mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
+ mHighlight.setRotation(v.getRotation());
+ mHighlight.setTranslationY(v.getTranslationY());
+ mHighlight.bringToFront();
+ v.bringToFront();
+ mStackSlider.setView(v);
+
+ v.setVisibility(VISIBLE);
+ }
+ }
+
+ @Override
+ @android.view.RemotableViewMethod
+ public void showNext() {
+ if (!mTransitionIsSetup) {
+ View v = getViewAtRelativeIndex(1);
+ if (v != null) {
+ setupStackSlider(v, StackSlider.NORMAL_MODE);
+ mStackSlider.setYProgress(0);
+ mStackSlider.setXProgress(0);
+ }
+ }
+ super.showNext();
+ }
+
+ @Override
+ @android.view.RemotableViewMethod
+ public void showPrevious() {
+ if (!mTransitionIsSetup) {
+ View v = getViewAtRelativeIndex(0);
+ if (v != null) {
+ setupStackSlider(v, StackSlider.NORMAL_MODE);
+ mStackSlider.setYProgress(1);
+ mStackSlider.setXProgress(0);
+ }
+ }
+ super.showPrevious();
+ }
+
private void transformViewAtIndex(int index, View view) {
float maxPerpectiveShift = mMeasuredHeight * PERSPECTIVE_SHIFT_FACTOR;
@@ -256,11 +299,12 @@ public class StackView extends AdapterViewAnimator {
super.showOnly(childIndex, animate, onLayout);
// Here we need to make sure that the z-order of the children is correct
- for (int i = mCurrentWindowEnd; i >= mCurrentWindowStart; i--) {
+ for (int i = mCurrentWindowEnd; i >= mCurrentWindowStart; i--) {
int index = modulo(i, getWindowSize());
View v = mViewsMap.get(index).view;
if (v != null) v.bringToFront();
}
+ mTransitionIsSetup = false;
}
private void updateChildTransforms() {
@@ -364,29 +408,24 @@ public class StackView extends AdapterViewAnimator {
activeIndex = (swipeGestureType == GESTURE_SLIDE_DOWN) ? 1 : 0;
}
+ int stackMode;
if (mLoopViews) {
- mStackSlider.setMode(StackSlider.NORMAL_MODE);
+ stackMode = StackSlider.NORMAL_MODE;
} else if (mCurrentWindowStartUnbounded + activeIndex == -1) {
activeIndex++;
- mStackSlider.setMode(StackSlider.BEGINNING_OF_STACK_MODE);
+ stackMode = StackSlider.BEGINNING_OF_STACK_MODE;
} else if (mCurrentWindowStartUnbounded + activeIndex == mAdapter.getCount() - 1) {
- mStackSlider.setMode(StackSlider.END_OF_STACK_MODE);
+ stackMode = StackSlider.END_OF_STACK_MODE;
} else {
- mStackSlider.setMode(StackSlider.NORMAL_MODE);
+ stackMode = StackSlider.NORMAL_MODE;
}
+ mTransitionIsSetup = stackMode == StackSlider.NORMAL_MODE;
+
View v = getViewAtRelativeIndex(activeIndex);
if (v == null) return;
- mHighlight.setImageBitmap(sHolographicHelper.createOutline(v));
- mHighlight.setRotation(v.getRotation());
- mHighlight.setTranslationY(v.getTranslationY());
- mHighlight.bringToFront();
- v.bringToFront();
- mStackSlider.setView(v);
-
- if (swipeGestureType == GESTURE_SLIDE_DOWN)
- v.setVisibility(VISIBLE);
+ setupStackSlider(v, stackMode);
// We only register this gesture if we've made it this far without a problem
mSwipeGestureType = swipeGestureType;
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index ff31dec..2949208 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -21,6 +21,7 @@ import com.android.internal.R;
import android.app.LocalActivityManager;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
@@ -63,6 +64,8 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode
private OnTabChangeListener mOnTabChangeListener;
private OnKeyListener mTabKeyListener;
+ private int mTabLayoutId;
+
public TabHost(Context context) {
super(context);
initTabHost();
@@ -70,6 +73,18 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode
public TabHost(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.TabWidget,
+ com.android.internal.R.attr.tabWidgetStyle, 0);
+
+ mTabLayoutId = a.getResourceId(R.styleable.TabWidget_tabLayout, 0);
+ if (mTabLayoutId == 0) {
+ throw new IllegalArgumentException("Invalid TabWidget tabLayout id");
+ }
+
+ a.recycle();
+
initTabHost();
}
@@ -214,6 +229,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) {
mTabWidget.setStripEnabled(false);
}
+
mTabWidget.addView(tabIndicator);
mTabSpecs.add(tabSpec);
@@ -513,7 +529,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
final Context context = getContext();
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View tabIndicator = inflater.inflate(R.layout.tab_indicator,
+ View tabIndicator = inflater.inflate(mTabLayoutId,
mTabWidget, // tab widget is the parent
false); // no inflate params
@@ -525,7 +541,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
}
-
+
return tabIndicator;
}
}
@@ -547,7 +563,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
final Context context = getContext();
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View tabIndicator = inflater.inflate(R.layout.tab_indicator,
+ View tabIndicator = inflater.inflate(mTabLayoutId,
mTabWidget, // tab widget is the parent
false); // no inflate params
@@ -555,14 +571,17 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
tv.setText(mLabel);
final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
- iconView.setImageDrawable(mIcon);
+ if (mIcon != null) {
+ iconView.setImageDrawable(mIcon);
+ iconView.setVisibility(VISIBLE);
+ }
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
// Donut apps get old color scheme
tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
}
-
+
return tabIndicator;
}
}
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 0469e7b..36adacd 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -120,7 +120,9 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
final Context context = mContext;
final Resources resources = context.getResources();
-
+
+ // Tests the target Sdk version, as set in the Manifest. Could not be set using styles.xml
+ // in a values-v? directory which targets the current platform Sdk version instead.
if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
// Donut apps get old color scheme
if (mLeftStrip == null) {
@@ -131,16 +133,6 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
mRightStrip = resources.getDrawable(
com.android.internal.R.drawable.tab_bottom_right_v4);
}
- } else {
- // Use modern color scheme for Eclair and beyond
- if (mLeftStrip == null) {
- mLeftStrip = resources.getDrawable(
- com.android.internal.R.drawable.tab_bottom_left);
- }
- if (mRightStrip == null) {
- mRightStrip = resources.getDrawable(
- com.android.internal.R.drawable.tab_bottom_right);
- }
}
// Deal with focus, as we don't want the focus to go by default
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 09563fc..257dbf0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6903,14 +6903,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.onTouchEvent(event);
- }
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.onTouchEvent(event);
- }
+ if (mInsertionPointCursorController != null) {
+ mInsertionPointCursorController.onTouchEvent(event);
+ }
+ if (mSelectionModifierCursorController != null) {
+ mSelectionModifierCursorController.onTouchEvent(event);
+ }
+ if (action == MotionEvent.ACTION_DOWN) {
// Reset this state; it will be re-set if super.onTouchEvent
// causes focus to move to the view.
mTouchFocusSelected = false;
@@ -6931,13 +6931,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
&& mText instanceof Spannable && mLayout != null) {
- if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.onTouchEvent(event);
- }
- if (mSelectionModifierCursorController != null) {
- mSelectionModifierCursorController.onTouchEvent(event);
- }
-
boolean handled = false;
// Save previous selection, in case this event is used to show the IME.
@@ -6946,7 +6939,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int oldScrollX = mScrollX;
final int oldScrollY = mScrollY;
-
+
if (mMovement != null) {
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
}
@@ -7142,6 +7135,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case Gravity.RIGHT:
return 0.0f;
case Gravity.CENTER_HORIZONTAL:
+ case Gravity.FILL_HORIZONTAL:
return (mLayout.getLineWidth(0) - ((mRight - mLeft) -
getCompoundPaddingLeft() - getCompoundPaddingRight())) /
getHorizontalFadingEdgeLength();
@@ -7980,7 +7974,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
mDrawable = mSelectHandleLeft;
handleWidth = mDrawable.getIntrinsicWidth();
- mHotspotX = handleWidth / 4 * 3;
+ mHotspotX = (handleWidth * 3) / 4;
break;
}
@@ -8263,6 +8257,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Whether selection anchors are active
private boolean mIsShowing;
+ // Double tap detection
+ private long mPreviousTapUpTime = 0;
+ private int mPreviousTapPositionX;
+ private int mPreviousTapPositionY;
+
private static final int DELAY_BEFORE_FADE_OUT = 4100;
private final Runnable mHider = new Runnable() {
@@ -8368,6 +8367,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Remember finger down position, to be able to start selection from there
mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
+ // Double tap detection
+ long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
+ if (duration <= ViewConfiguration.getDoubleTapTimeout()) {
+ final int deltaX = x - mPreviousTapPositionX;
+ final int deltaY = y - mPreviousTapPositionY;
+ final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
+ final int doubleTapSlop =
+ ViewConfiguration.get(getContext()).getScaledDoubleTapSlop();
+ final int slopSquared = doubleTapSlop * doubleTapSlop;
+ if (distanceSquared < slopSquared) {
+ startSelectionActionMode();
+ // Hacky: onTapUpEvent will open a context menu with cut/copy
+ // Prevent this by hiding handles which will be revived instead.
+ hide();
+ }
+ }
+
+ mPreviousTapPositionX = x;
+ mPreviousTapPositionY = y;
+
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -8379,6 +8398,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
updateMinAndMaxOffsets(event);
}
break;
+
+ case MotionEvent.ACTION_UP:
+ mPreviousTapUpTime = SystemClock.uptimeMillis();
+ break;
}
}
return false;
diff --git a/core/java/com/android/internal/nfc/LlcpServiceSocket.java b/core/java/com/android/internal/nfc/LlcpServiceSocket.java
index 4607527..318982b 100644
--- a/core/java/com/android/internal/nfc/LlcpServiceSocket.java
+++ b/core/java/com/android/internal/nfc/LlcpServiceSocket.java
@@ -78,8 +78,9 @@ public class LlcpServiceSocket {
* @param handle
* The handle returned by the NFC service and used to identify
* the socket in subsequent calls.
+ * @hide
*/
- LlcpServiceSocket(ILlcpServiceSocket service, ILlcpSocket socketService, int handle) {
+ public LlcpServiceSocket(ILlcpServiceSocket service, ILlcpSocket socketService, int handle) {
this.mService = service;
this.mHandle = handle;
this.mLlcpSocketService = socketService;
diff --git a/core/java/com/android/internal/nfc/LlcpSocket.java b/core/java/com/android/internal/nfc/LlcpSocket.java
index ae74002..b1b1320 100644
--- a/core/java/com/android/internal/nfc/LlcpSocket.java
+++ b/core/java/com/android/internal/nfc/LlcpSocket.java
@@ -78,8 +78,9 @@ public class LlcpSocket {
* @param handle
* The handle returned by the NFC service and used to identify
* the socket in subsequent calls.
+ * @hide
*/
- LlcpSocket(ILlcpSocket service, int handle) {
+ public LlcpSocket(ILlcpSocket service, int handle) {
this.mService = service;
this.mHandle = handle;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index da0c5a2..c473fd2 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -681,19 +681,6 @@ class ZygoteConnection {
throws ZygoteInit.MethodAndArgsCaller {
/*
- * First, set the capabilities if necessary
- */
-
- if (parsedArgs.uid != 0) {
- try {
- ZygoteInit.setCapabilities(parsedArgs.permittedCapabilities,
- parsedArgs.effectiveCapabilities);
- } catch (IOException ex) {
- Log.e(TAG, "Error setting capabilities", ex);
- }
- }
-
- /*
* Close the socket, unless we're in "peer wait" mode, in which
* case it's used to track the liveness of this process.
*/
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 1d54c61..848bf9e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -492,18 +492,6 @@ public class ZygoteInit {
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
- /*
- * First, set the capabilities if necessary
- */
-
- if (parsedArgs.uid != 0) {
- try {
- setCapabilities(parsedArgs.permittedCapabilities,
- parsedArgs.effectiveCapabilities);
- } catch (IOException ex) {
- Log.e(TAG, "Error setting capabilities", ex);
- }
- }
closeServerSocket();
@@ -552,7 +540,9 @@ public class ZygoteInit {
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids, debugFlags, null);
+ parsedArgs.gids, debugFlags, null,
+ parsedArgs.permittedCapabilities,
+ parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index bffec1d..49ae2bc 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -18,6 +18,7 @@ package com.android.internal.view;
import android.os.ResultReceiver;
import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import com.android.internal.view.InputBindResult;
import com.android.internal.view.IInputContext;
@@ -54,6 +55,7 @@ interface IInputMethodManager {
void hideMySoftInput(in IBinder token, int flags);
void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
+ InputMethodSubtype getCurrentInputMethodSubtype();
boolean setInputMethodEnabled(String id, boolean enabled);
}
diff --git a/core/java/com/android/internal/view/InputMethodAndSubtypeEnabler.java b/core/java/com/android/internal/view/InputMethodAndSubtypeEnabler.java
deleted file mode 100644
index 200d49f..0000000
--- a/core/java/com/android/internal/view/InputMethodAndSubtypeEnabler.java
+++ /dev/null
@@ -1,319 +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 com.android.internal.view;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
-public class InputMethodAndSubtypeEnabler extends PreferenceActivity {
-
- private boolean mHaveHardKeyboard;
-
- private List<InputMethodInfo> mInputMethodProperties;
-
- private final TextUtils.SimpleStringSplitter mStringColonSplitter
- = new TextUtils.SimpleStringSplitter(':');
-
- private String mLastInputMethodId;
- private String mLastTickedInputMethodId;
-
- private AlertDialog mDialog = null;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- Configuration config = getResources().getConfiguration();
- mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
- onCreateIMM();
- setPreferenceScreen(createPreferenceHierarchy());
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- loadInputMethodSubtypeList();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- saveInputMethodSubtypeList();
- }
-
- @Override
- public boolean onPreferenceTreeClick(
- PreferenceScreen preferenceScreen, Preference preference) {
-
- if (preference instanceof CheckBoxPreference) {
- final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
- final String id = chkPref.getKey();
- // TODO: Check subtype or not here
- if (chkPref.isChecked()) {
- InputMethodInfo selImi = null;
- final int N = mInputMethodProperties.size();
- for (int i = 0; i < N; i++) {
- InputMethodInfo imi = mInputMethodProperties.get(i);
- if (id.equals(imi.getId())) {
- selImi = imi;
- if (isSystemIme(imi)) {
- setSubtypesPreferenceEnabled(id, true);
- // This is a built-in IME, so no need to warn.
- mLastTickedInputMethodId = id;
- return super.onPreferenceTreeClick(preferenceScreen, preference);
- }
- break;
- }
- }
- if (selImi == null) {
- return super.onPreferenceTreeClick(preferenceScreen, preference);
- }
- chkPref.setChecked(false);
- if (mDialog == null) {
- mDialog = (new AlertDialog.Builder(this))
- .setTitle(android.R.string.dialog_alert_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setCancelable(true)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- chkPref.setChecked(true);
- setSubtypesPreferenceEnabled(id, true);
- mLastTickedInputMethodId = id;
- }
-
- })
- .setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- }
-
- })
- .create();
- } else {
- if (mDialog.isShowing()) {
- mDialog.dismiss();
- }
- }
- mDialog.setMessage(getResources().getString(
- com.android.internal.R.string.ime_enabler_security_warning,
- selImi.getServiceInfo().applicationInfo.loadLabel(getPackageManager())));
- mDialog.show();
- } else {
- if (id.equals(mLastTickedInputMethodId)) {
- mLastTickedInputMethodId = null;
- }
- setSubtypesPreferenceEnabled(id, false);
- }
- }
- return super.onPreferenceTreeClick(preferenceScreen, preference);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mDialog != null) {
- mDialog.dismiss();
- mDialog = null;
- }
- }
-
- private void onCreateIMM() {
- InputMethodManager imm = (InputMethodManager) getSystemService(
- Context.INPUT_METHOD_SERVICE);
-
- // TODO: Change mInputMethodProperties to Map
- mInputMethodProperties = imm.getInputMethodList();
-
- mLastInputMethodId = Settings.Secure.getString(getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD);
- }
-
- private PreferenceScreen createPreferenceHierarchy() {
- // Root
- PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
-
- int N = (mInputMethodProperties == null ? 0 : mInputMethodProperties.size());
- // TODO: Use iterator.
- for (int i = 0; i < N; ++i) {
- PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(this);
- root.addPreference(keyboardSettingsCategory);
- InputMethodInfo property = mInputMethodProperties.get(i);
- String prefKey = property.getId();
-
- PackageManager pm = getPackageManager();
- CharSequence label = property.loadLabel(pm);
- boolean systemIME = isSystemIme(property);
-
- keyboardSettingsCategory.setTitle(label);
-
- // Add a check box.
- // Don't show the toggle if it's the only keyboard in the system, or it's a system IME.
- if (mHaveHardKeyboard || (N > 1 && !systemIME)) {
- CheckBoxPreference chkbxPref = new CheckBoxPreference(this);
- chkbxPref.setKey(prefKey);
- chkbxPref.setTitle(label);
- keyboardSettingsCategory.addPreference(chkbxPref);
- }
-
- ArrayList<InputMethodSubtype> subtypes = property.getSubtypes();
- if (subtypes.size() > 0) {
- PreferenceCategory subtypesCategory = new PreferenceCategory(this);
- subtypesCategory.setTitle(getResources().getString(
- com.android.internal.R.string.ime_enabler_subtype_title, label));
- root.addPreference(subtypesCategory);
- for (InputMethodSubtype subtype: subtypes) {
- CharSequence subtypeLabel;
- int nameResId = subtype.getNameResId();
- if (nameResId != 0) {
- subtypeLabel = pm.getText(property.getPackageName(), nameResId,
- property.getServiceInfo().applicationInfo);
- } else {
- int modeResId = subtype.getModeResId();
- CharSequence language = subtype.getLocale();
- CharSequence mode = modeResId == 0 ? null
- : pm.getText(property.getPackageName(), modeResId,
- property.getServiceInfo().applicationInfo);
- // TODO: Use more friendly Title and UI
- subtypeLabel = (mode == null ? "" : mode) + ","
- + (language == null ? "" : language);
- }
- CheckBoxPreference chkbxPref = new CheckBoxPreference(this);
- chkbxPref.setKey(prefKey + subtype.hashCode());
- chkbxPref.setTitle(subtypeLabel);
- chkbxPref.setSummary(label);
- subtypesCategory.addPreference(chkbxPref);
- }
- }
- }
- return root;
- }
-
- private void loadInputMethodSubtypeList() {
- final HashSet<String> enabled = new HashSet<String>();
- String enabledStr = Settings.Secure.getString(getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS);
- if (enabledStr != null) {
- final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
- splitter.setString(enabledStr);
- while (splitter.hasNext()) {
- enabled.add(splitter.next());
- }
- }
-
- // Update the statuses of the Check Boxes.
- int N = mInputMethodProperties.size();
- // TODO: Use iterator.
- for (int i = 0; i < N; ++i) {
- final String id = mInputMethodProperties.get(i).getId();
- CheckBoxPreference pref = (CheckBoxPreference) findPreference(
- mInputMethodProperties.get(i).getId());
- if (pref != null) {
- boolean isEnabled = enabled.contains(id);
- pref.setChecked(isEnabled);
- setSubtypesPreferenceEnabled(id, isEnabled);
- }
- }
- mLastTickedInputMethodId = null;
- }
-
- private void saveInputMethodSubtypeList() {
- StringBuilder builder = new StringBuilder();
- StringBuilder disabledSysImes = new StringBuilder();
-
- int firstEnabled = -1;
- int N = mInputMethodProperties.size();
- for (int i = 0; i < N; ++i) {
- final InputMethodInfo property = mInputMethodProperties.get(i);
- final String id = property.getId();
- CheckBoxPreference pref = (CheckBoxPreference) findPreference(id);
- boolean currentInputMethod = id.equals(mLastInputMethodId);
- boolean systemIme = isSystemIme(property);
- // TODO: Append subtypes by using the separator ";"
- if (((N == 1 || systemIme) && !mHaveHardKeyboard)
- || (pref != null && pref.isChecked())) {
- if (builder.length() > 0) builder.append(':');
- builder.append(id);
- if (firstEnabled < 0) {
- firstEnabled = i;
- }
- } else if (currentInputMethod) {
- mLastInputMethodId = mLastTickedInputMethodId;
- }
- // If it's a disabled system ime, add it to the disabled list so that it
- // doesn't get enabled automatically on any changes to the package list
- if (pref != null && !pref.isChecked() && systemIme && mHaveHardKeyboard) {
- if (disabledSysImes.length() > 0) disabledSysImes.append(":");
- disabledSysImes.append(id);
- }
- }
-
- // If the last input method is unset, set it as the first enabled one.
- if (TextUtils.isEmpty(mLastInputMethodId)) {
- if (firstEnabled >= 0) {
- mLastInputMethodId = mInputMethodProperties.get(firstEnabled).getId();
- } else {
- mLastInputMethodId = null;
- }
- }
-
- Settings.Secure.putString(getContentResolver(),
- Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
- Settings.Secure.putString(getContentResolver(),
- Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, disabledSysImes.toString());
- Settings.Secure.putString(getContentResolver(),
- Settings.Secure.DEFAULT_INPUT_METHOD,
- mLastInputMethodId != null ? mLastInputMethodId : "");
- }
-
- private void setSubtypesPreferenceEnabled(String id, boolean enabled) {
- PreferenceScreen preferenceScreen = getPreferenceScreen();
- final int N = mInputMethodProperties.size();
- // TODO: Use iterator.
- for (int i = 0; i < N; i++) {
- InputMethodInfo imi = mInputMethodProperties.get(i);
- if (id.equals(imi.getId())) {
- for (InputMethodSubtype subtype: imi.getSubtypes()) {
- preferenceScreen.findPreference(id + subtype.hashCode()).setEnabled(enabled);
- }
- }
- }
- }
-
- private boolean isSystemIme(InputMethodInfo property) {
- return (property.getServiceInfo().applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) != 0;
- }
-}