diff options
49 files changed, 922 insertions, 301 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 6849fd766035..6d5686a40c33 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -227,6 +227,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case WILL_ACTIVITY_BE_VISIBLE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + boolean res = willActivityBeVisible(token); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + case REGISTER_RECEIVER_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); @@ -1360,6 +1369,18 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + public boolean willActivityBeVisible(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(WILL_ACTIVITY_BE_VISIBLE_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return res; + } public Intent registerReceiver(IApplicationThread caller, IIntentReceiver receiver, IntentFilter filter, String perm) throws RemoteException diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 7b5b63e1d21a..35d1948d8703 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3095,9 +3095,6 @@ public final class ActivityThread { r.paused = false; r.stopped = false; - if (r.activity.mStartedActivity) { - r.hideForNow = true; - } r.state = null; } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { @@ -3132,7 +3129,15 @@ public final class ActivityThread { // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. - if (r.window == null && !a.mFinished && !a.mStartedActivity) { + boolean willBeVisible = !a.mStartedActivity; + if (!willBeVisible) { + try { + willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( + a.getActivityToken()); + } catch (RemoteException e) { + } + } + if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); @@ -3148,8 +3153,8 @@ public final class ActivityThread { // If the window has already been added, but during resume // we started another activity, then don't yet make the - // window visisble. - } else if (a.mStartedActivity) { + // window visible. + } else if (!willBeVisible) { if (localLOGV) Log.v( TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; @@ -3157,7 +3162,7 @@ public final class ActivityThread { // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. - if (!r.activity.mFinished && !a.mStartedActivity + if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.newConfig != null) { if (DEBUG_CONFIGURATION) Log.v(TAG, "Resuming activity " diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 3913ed5da0a2..14571defc6fa 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -97,6 +97,7 @@ public interface IActivityManager extends IInterface { public boolean finishActivity(IBinder token, int code, Intent data) throws RemoteException; public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException; + public boolean willActivityBeVisible(IBinder token) throws RemoteException; public Intent registerReceiver(IApplicationThread caller, IIntentReceiver receiver, IntentFilter filter, String requiredPermission) throws RemoteException; @@ -501,4 +502,5 @@ public interface IActivityManager extends IInterface { int KILL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+102; int IS_USER_A_MONKEY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+103; int START_ACTIVITY_AND_WAIT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+104; + int WILL_ACTIVITY_BE_VISIBLE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+105; } diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java index 4b4cc050dcbe..84a57b526a6d 100644 --- a/core/java/android/app/ListActivity.java +++ b/core/java/android/app/ListActivity.java @@ -51,31 +51,31 @@ import android.widget.ListView; * The following code demonstrates an (ugly) custom screen layout. It has a list * with a green background, and an alternate red "no data" message. * </p> - * + * * <pre> * <?xml version="1.0" encoding="utf-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:orientation="vertical" - * android:layout_width="match_parent" + * android:layout_width="match_parent" * android:layout_height="match_parent" * android:paddingLeft="8dp" * android:paddingRight="8dp"> - * + * * <ListView android:id="@id/android:list" - * android:layout_width="match_parent" + * android:layout_width="match_parent" * android:layout_height="match_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> - * + * * <TextView id="@id/android:empty" - * android:layout_width="match_parent" + * android:layout_width="match_parent" * android:layout_height="match_parent" * android:background="#FF0000" * android:text="No data"/> * </LinearLayout> * </pre> - * + * * <p> * <strong>Row Layout</strong> * </p> @@ -96,27 +96,27 @@ import android.widget.ListView; * source for the resource two_line_list_item, which displays two data * fields,one above the other, for each list row. * </p> - * + * * <pre> * <?xml version="1.0" encoding="utf-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:layout_width="match_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> - * + * * <TextView android:id="@+id/text1" * android:textSize="16sp" * android:textStyle="bold" * android:layout_width="match_parent" * android:layout_height="wrap_content"/> - * + * * <TextView android:id="@+id/text2" * android:textSize="16sp" * android:layout_width="match_parent" * android:layout_height="wrap_content"/> * </LinearLayout> * </pre> - * + * * <p> * You must identify the data bound to each TextView object in this layout. The * syntax for this is discussed in the next section. @@ -137,40 +137,40 @@ import android.widget.ListView; * Contacts provider for all contacts, then binding the Name and Company fields * to a two line row layout in the activity's ListView. * </p> - * + * * <pre> * public class MyListAdapter extends ListActivity { - * + * * @Override * protected void onCreate(Bundle savedInstanceState){ * super.onCreate(savedInstanceState); - * + * * // We'll define a custom screen layout here (the one shown above), but * // typically, you could just use the standard ListActivity layout. * setContentView(R.layout.custom_list_activity_view); - * + * * // Query for all people contacts using the {@link android.provider.Contacts.People} convenience class. * // Put a managed wrapper around the retrieved cursor so we don't have to worry about * // requerying or closing it as the activity changes state. * mCursor = this.getContentResolver().query(People.CONTENT_URI, null, null, null, null); * startManagingCursor(mCursor); - * - * // Now create a new list adapter bound to the cursor. + * + * // Now create a new list adapter bound to the cursor. * // SimpleListAdapter is designed for binding to a Cursor. * ListAdapter adapter = new SimpleCursorAdapter( * this, // Context. - * android.R.layout.two_line_list_item, // Specify the row template to use (here, two columns bound to the two retrieved cursor + * android.R.layout.two_line_list_item, // Specify the row template to use (here, two columns bound to the two retrieved cursor * rows). * mCursor, // Pass in the cursor to bind to. * new String[] {People.NAME, People.COMPANY}, // Array of cursor columns to bind to. * new int[] {android.R.id.text1, android.R.id.text2}); // Parallel array of which template objects to bind to those columns. - * + * * // Bind to our new adapter. * setListAdapter(adapter); * } * } * </pre> - * + * * @see #setListAdapter * @see android.widget.ListView */ @@ -194,13 +194,13 @@ public class ListActivity extends Activity { mList.focusableViewAvailable(mList); } }; - + /** * This method will be called when an item in the list is selected. * Subclasses should override. Subclasses can call * getListView().getItemAtPosition(position) if they need to access the * data associated with the selected item. - * + * * @param l The ListView where the click happened * @param v The view that was clicked within the ListView * @param position The position of the view in the list @@ -208,11 +208,11 @@ public class ListActivity extends Activity { */ protected void onListItemClick(ListView l, View v, int position, long id) { } - + /** * Ensures the list view has been created before Activity restores all * of the view states. - * + * *@see Activity#onRestoreInstanceState(Bundle) */ @Override @@ -222,9 +222,18 @@ public class ListActivity extends Activity { } /** + * @see Activity#onDestroy() + */ + @Override + protected void onDestroy() { + mHandler.removeCallbacks(mRequestFocus); + super.onDestroy(); + } + + /** * Updates the screen state (current list and other views) when the * content changes. - * + * * @see Activity#onContentChanged() */ @Override @@ -262,7 +271,7 @@ public class ListActivity extends Activity { /** * Set the currently selected list item to the specified * position with the adapter's data - * + * * @param position */ public void setSelection(int position) { @@ -290,7 +299,7 @@ public class ListActivity extends Activity { ensureList(); return mList; } - + /** * Get the ListAdapter associated with this activity's ListView. */ @@ -303,7 +312,7 @@ public class ListActivity extends Activity { return; } setContentView(com.android.internal.R.layout.list_content); - + } private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { @@ -313,4 +322,3 @@ public class ListActivity extends Activity { } }; } - diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 54fc0447e495..65982642d88a 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -597,12 +597,18 @@ public final class Configuration implements Parcelable, Comparable<Configuration if (n != 0) return n; n = this.mnc - that.mnc; if (n != 0) return n; - n = this.locale.getLanguage().compareTo(that.locale.getLanguage()); - if (n != 0) return n; - n = this.locale.getCountry().compareTo(that.locale.getCountry()); - if (n != 0) return n; - n = this.locale.getVariant().compareTo(that.locale.getVariant()); - if (n != 0) return n; + if (this.locale == null) { + if (that.locale != null) return 1; + } else if (that.locale == null) { + return -1; + } else { + n = this.locale.getLanguage().compareTo(that.locale.getLanguage()); + if (n != 0) return n; + n = this.locale.getCountry().compareTo(that.locale.getCountry()); + if (n != 0) return n; + n = this.locale.getVariant().compareTo(that.locale.getVariant()); + if (n != 0) return n; + } n = this.touchscreen - that.touchscreen; if (n != 0) return n; n = this.keyboard - that.keyboard; @@ -640,7 +646,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration public int hashCode() { return ((int)this.fontScale) + this.mcc + this.mnc - + this.locale.hashCode() + this.touchscreen + + (this.locale != null ? this.locale.hashCode() : 0) + + this.touchscreen + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden + this.navigation + this.navigationHidden + this.orientation + this.screenLayout + this.uiMode; diff --git a/core/java/android/pim/vcard/VCardEntryCommitter.java b/core/java/android/pim/vcard/VCardEntryCommitter.java index 3cd64b084866..2f99e4a36514 100644 --- a/core/java/android/pim/vcard/VCardEntryCommitter.java +++ b/core/java/android/pim/vcard/VCardEntryCommitter.java @@ -19,6 +19,8 @@ import android.content.ContentResolver; import android.net.Uri; import android.util.Log; +import java.util.ArrayList; + /** * <P> * {@link VCardEntryHandler} implementation which commits the entry to ContentResolver. @@ -35,7 +37,7 @@ public class VCardEntryCommitter implements VCardEntryHandler { private final ContentResolver mContentResolver; private long mTimeToCommit; - private Uri mLastCreatedUri; + private ArrayList<Uri> mCreatedUris = new ArrayList<Uri>(); public VCardEntryCommitter(ContentResolver resolver) { mContentResolver = resolver; @@ -52,11 +54,21 @@ public class VCardEntryCommitter implements VCardEntryHandler { public void onEntryCreated(final VCardEntry contactStruct) { long start = System.currentTimeMillis(); - mLastCreatedUri = contactStruct.pushIntoContentResolver(mContentResolver); + mCreatedUris.add(contactStruct.pushIntoContentResolver(mContentResolver)); mTimeToCommit += System.currentTimeMillis() - start; } + // TODO: Compatibility function to not break the build. Will be removed shortly + @Deprecated public Uri getLastCreatedUri() { - return mLastCreatedUri; + return mCreatedUris.size() == 0 ? null : mCreatedUris.get(mCreatedUris.size() - 1); + } + + /** + * Returns the list of created Uris. This list should not be modified by the caller as it is + * not a clone. + */ + public ArrayList<Uri> getCreatedUris() { + return mCreatedUris; } }
\ No newline at end of file diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index a5c85c12aeb3..4377a2b997f4 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -363,7 +363,7 @@ public class Browser { str[i] = c.getString(0); i++; } - c.deactivate(); + c.close(); return str; } catch (IllegalStateException e) { return new String[0]; @@ -402,7 +402,7 @@ public class Browser { if (!c.moveToNext()) break; } } - c.deactivate(); + c.close(); } catch (IllegalStateException e) { Log.e(LOGTAG, "truncateHistory", e); return; @@ -427,7 +427,7 @@ public class Browser { null ); boolean ret = c.moveToFirst(); - c.deactivate(); + c.close(); return ret; } catch (IllegalStateException e) { return false; @@ -462,7 +462,7 @@ public class Browser { null, null); if (!c.moveToFirst()) { - c.deactivate(); + c.close(); return; } @@ -489,7 +489,7 @@ public class Browser { iconDb.releaseIconForPageUrl(url); } } while (c.moveToNext()); - c.deactivate(); + c.close(); if (!firstTime) { ContentValues map = new ContentValues(); @@ -576,7 +576,7 @@ public class Browser { } else { cr.insert(SEARCHES_URI, map); } - c.deactivate(); + c.close(); } catch (IllegalStateException e) { Log.e(LOGTAG, "addSearchUrl", e); return; diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index c06a1e2245ad..40a408a12ee8 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -887,6 +887,25 @@ public final class ContactsContract { "as_vcard"); /** + * Base {@link Uri} for referencing multiple {@link Contacts} entry, + * created by appending {@link #LOOKUP_KEY} using + * {@link Uri#withAppendedPath(Uri, String)}. The lookup keys have to be + * encoded and joined with the colon (":") seperator. The resulting string + * has to be encoded again. Provides + * {@link OpenableColumns} columns when queried, or returns the + * referenced contact formatted as a vCard when opened through + * {@link ContentResolver#openAssetFileDescriptor(Uri, String)}. + * + * This is private API because we do not have a well-defined way to + * specify several entities yet. The format of this Uri might change in the future + * or the Uri might be completely removed. + * + * @hide + */ + public static final Uri CONTENT_MULTI_VCARD_URI = Uri.withAppendedPath(CONTENT_URI, + "as_multi_vcard"); + + /** * Builds a {@link #CONTENT_LOOKUP_URI} style {@link Uri} describing the * requested {@link Contacts} entry. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 873e1a6feed2..503d8b8da1dd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2900,6 +2900,15 @@ public final class Settings { */ public static final String DROPBOX_TAG_PREFIX = "dropbox:"; + /** + * Lines of logcat to include with system crash/ANR/etc. reports, + * as a prefix of the dropbox tag of the report type. + * For example, "logcat_for_system_server_anr" controls the lines + * of logcat captured with system server ANR reports. 0 to disable. + * @hide + */ + public static final String ERROR_LOGCAT_PREFIX = + "logcat_for_"; /** diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java index f320701d5454..2f429ffccf62 100644 --- a/core/java/android/text/style/LeadingMarginSpan.java +++ b/core/java/android/text/style/LeadingMarginSpan.java @@ -68,7 +68,20 @@ extends ParagraphStyle boolean first, Layout layout); + /** + * An extended version of {@link LeadingMarginSpan}, which allows + * the implementor to specify the number of lines of text to which + * this object is attached that the "first line of paragraph" margin + * width will be applied to. + */ public interface LeadingMarginSpan2 extends LeadingMarginSpan, WrapTogetherSpan { + /** + * Returns the number of lines of text to which this object is + * attached that the "first line" margin will apply to. + * Note that if this returns N, the first N lines of the region, + * not the first N lines of each paragraph, will be given the + * special margin width. + */ public int getLeadingMarginLineCount(); }; diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index d8b6d1f47392..ff34f4add39d 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -18,6 +18,7 @@ package android.view; import android.content.Context; import android.util.DisplayMetrics; +import android.util.FloatMath; /** * Detects transformation gestures involving more than one pointer ("multitouch") @@ -96,14 +97,16 @@ public class ScaleGestureDetector { * A convenience class to extend when you only want to listen for a subset * of scaling-related events. This implements all methods in * {@link OnScaleGestureListener} but does nothing. - * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and - * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return + * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns + * {@code false} so that a subclass can retrieve the accumulated scale + * factor in an overridden onScaleEnd. + * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns * {@code true}. */ public static class SimpleOnScaleGestureListener implements OnScaleGestureListener { public boolean onScale(ScaleGestureDetector detector) { - return true; + return false; } public boolean onScaleBegin(ScaleGestureDetector detector) { @@ -115,10 +118,19 @@ public class ScaleGestureDetector { } } + /** + * This value is the threshold ratio between our previous combined pressure + * and the current combined pressure. We will only fire an onScale event if + * the computed ratio between the current and previous event pressures is + * greater than this value. When pressure decreases rapidly between events + * the position values can often be imprecise, as it usually indicates + * that the user is in the process of lifting a pointer off of the device. + * Its value was tuned experimentally. + */ private static final float PRESSURE_THRESHOLD = 0.67f; - private Context mContext; - private OnScaleGestureListener mListener; + private final Context mContext; + private final OnScaleGestureListener mListener; private boolean mGestureInProgress; private MotionEvent mPrevEvent; @@ -137,7 +149,7 @@ public class ScaleGestureDetector { private float mPrevPressure; private long mTimeDelta; - private float mEdgeSlop; + private final float mEdgeSlop; private float mRightSlopEdge; private float mBottomSlopEdge; private boolean mSloppyGesture; @@ -410,7 +422,7 @@ public class ScaleGestureDetector { if (mCurrLen == -1) { final float cvx = mCurrFingerDiffX; final float cvy = mCurrFingerDiffY; - mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy); + mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy); } return mCurrLen; } @@ -425,7 +437,7 @@ public class ScaleGestureDetector { if (mPrevLen == -1) { final float pvx = mPrevFingerDiffX; final float pvy = mPrevFingerDiffY; - mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy); + mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy); } return mPrevLen; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 54c805fc7984..e83b9b5a53a7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1775,6 +1775,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private CheckForLongPress mPendingCheckForLongPress; private CheckForTap mPendingCheckForTap = null; + private PerformClick mPerformClick; private UnsetPressedState mUnsetPressedState; @@ -4330,7 +4331,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility // Only perform take click actions if we were in the pressed state if (!focusTaken) { - performClick(); + // Use a Runnable and post this rather than calling + // performClick directly. This lets other visual state + // of the view update before click actions start. + if (mPerformClick == null) { + mPerformClick = new PerformClick(); + } + if (!post(mPerformClick)) { + performClick(); + } } } @@ -8965,6 +8974,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } } + private final class PerformClick implements Runnable { + public void run() { + performClick(); + } + } + /** * Interface definition for a callback to be invoked when a key event is * dispatched to this view. The callback will be invoked before the key diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 825ea464186d..c5a4e9eebe6d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -535,9 +535,10 @@ public class WebView extends AbsoluteLayout static final int SET_ROOT_LAYER_MSG_ID = 124; static final int RETURN_LABEL = 125; static final int FIND_AGAIN = 126; + static final int CENTER_FIT_RECT = 127; private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; - private static final int LAST_PACKAGE_MSG_ID = FIND_AGAIN; + private static final int LAST_PACKAGE_MSG_ID = CENTER_FIT_RECT; static final String[] HandlerPrivateDebugString = { "REMEMBER_PASSWORD", // = 1; @@ -578,7 +579,8 @@ public class WebView extends AbsoluteLayout "IMMEDIATE_REPAINT_MSG_ID", // = 123; "SET_ROOT_LAYER_MSG_ID", // = 124; "RETURN_LABEL", // = 125; - "FIND_AGAIN" // = 126; + "FIND_AGAIN", // = 126; + "CENTER_FIT_RECT" // = 127; }; // If the site doesn't use the viewport meta tag to specify the viewport, @@ -2301,15 +2303,18 @@ public class WebView extends AbsoluteLayout scrollBar.draw(canvas); } + private boolean canOverscrollHorizontally() { + return (Math.abs(mMinZoomScale - mMaxZoomScale) >= MINIMUM_SCALE_INCREMENT) + && getSettings().supportZoom() + && getSettings().getUseWideViewPort(); + } + @Override protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { mInOverScrollMode = false; int maxX = computeMaxScrollX(); - if (maxX == 0 && (Math.abs(mMinZoomScale - mMaxZoomScale) - < MINIMUM_SCALE_INCREMENT) - || !getSettings().supportZoom() - || !getSettings().getUseWideViewPort()) { + if (maxX == 0 && !canOverscrollHorizontally()) { // do not over scroll x if the page just fits the screen and it // can't zoom or the view doesn't use wide viewport scrollX = pinLocX(scrollX); @@ -3115,6 +3120,14 @@ public class WebView extends AbsoluteLayout return; } + // if both mContentWidth and mContentHeight are 0, it means there is no + // valid Picture passed to WebView yet. This can happen when WebView + // just starts. Draw the background and return. + if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) { + canvas.drawColor(mBackgroundColor); + return; + } + int saveCount = canvas.save(); if (mInOverScrollMode && getSettings().getUseSystemOverscrollBackground()) { @@ -4207,7 +4220,7 @@ public class WebView extends AbsoluteLayout // Textfields and plugins need to receive the shift up key even if // another key was released while the shift key was held down. - if (!inEditingMode() && !nativeFocusIsPlugin()) { + if (!inEditingMode() && (mNativeClass == 0 || !nativeFocusIsPlugin())) { if (event.getAction() == KeyEvent.ACTION_DOWN) { mGotKeyDown = true; } else { @@ -5112,8 +5125,10 @@ public class WebView extends AbsoluteLayout if (ev.getY() < 0) pageUp(true); return true; } + boolean shiftPressed = mShiftIsPressed && (mNativeClass == 0 + || !nativeFocusIsPlugin()); if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if (mShiftIsPressed && !nativeFocusIsPlugin()) { + if (shiftPressed) { return true; // discard press if copy in progress } mTrackballDown = true; @@ -5138,7 +5153,7 @@ public class WebView extends AbsoluteLayout mPrivateHandler.removeMessages(LONG_PRESS_CENTER); mTrackballDown = false; mTrackballUpTime = time; - if (mShiftIsPressed && !nativeFocusIsPlugin()) { + if (shiftPressed) { if (mExtendSelection) { commitCopy(); } else { @@ -5276,7 +5291,7 @@ public class WebView extends AbsoluteLayout float yRate = mTrackballRemainsY * 1000 / elapsed; int viewWidth = getViewWidth(); int viewHeight = getViewHeight(); - if (mShiftIsPressed && !nativeFocusIsPlugin()) { + if (mShiftIsPressed && (mNativeClass == 0 || !nativeFocusIsPlugin())) { moveSelection(scaleTrackballX(xRate, viewWidth), scaleTrackballY(yRate, viewHeight)); mTrackballRemainsX = mTrackballRemainsY = 0; @@ -5314,7 +5329,7 @@ public class WebView extends AbsoluteLayout + " mTrackballRemainsX=" + mTrackballRemainsX + " mTrackballRemainsY=" + mTrackballRemainsY); } - if (nativeFocusIsPlugin()) { + if (mNativeClass != 0 && nativeFocusIsPlugin()) { for (int i = 0; i < count; i++) { letPluginHandleNavKey(selectKeyCode, time, true); } @@ -5381,7 +5396,9 @@ public class WebView extends AbsoluteLayout vx = 0; } } - + if (maxX == 0 && !canOverscrollHorizontally()) { + vx = 0; + } if (true /* EMG release: make our fling more like Maps' */) { // maps cuts their velocity in half vx = vx * 3 / 4; @@ -5722,49 +5739,50 @@ public class WebView extends AbsoluteLayout } /* - * Maximize and center the view inside the WebView. If the zoom doesn't need - * to be changed, do an animated scroll to center it. If the zoom needs to - * be changed, find the zoom center and do a smooth zoom transition. + * Maximize and center the rectangle, specified in the document coordinate + * space, inside the WebView. If the zoom doesn't need to be changed, do an + * animated scroll to center it. If the zoom needs to be changed, find the + * zoom center and do a smooth zoom transition. */ - private void centerPluginOnScreen(ViewManager.ChildView child) { + private void centerFitRect(int docX, int docY, int docWidth, int docHeight) { int viewWidth = getViewWidth(); int viewHeight = getViewHeightWithTitle(); - float scale = Math.min((float) viewWidth / child.width, - (float) viewHeight / child.height); + float scale = Math.min((float) viewWidth / docWidth, (float) viewHeight + / docHeight); if (scale < mMinZoomScale) { scale = mMinZoomScale; } else if (scale > mMaxZoomScale) { scale = mMaxZoomScale; } if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) { - pinScrollTo( - contentToViewX(child.x + child.width / 2) - viewWidth / 2, - contentToViewY(child.y + child.height / 2) - viewHeight / 2, + pinScrollTo(contentToViewX(docX + docWidth / 2) - viewWidth / 2, + contentToViewY(docY + docHeight / 2) - viewHeight / 2, true, 0); } else { - int oldScreenX = contentToViewX(child.x) - mScrollX; - int newPluginX = (int) (child.x * scale); - int newPluginWidth = (int) (child.width * scale); + int oldScreenX = contentToViewX(docX) - mScrollX; + int rectViewX = (int) (docX * scale); + int rectViewWidth = (int) (docWidth * scale); int newMaxWidth = (int) (mContentWidth * scale); - int newScreenX = (viewWidth - newPluginWidth) / 2; + int newScreenX = (viewWidth - rectViewWidth) / 2; // pin the newX to the WebView - if (newScreenX > newPluginX) { - newScreenX = newPluginX; - } else if (newScreenX > (newMaxWidth - newPluginX - newPluginWidth)) { - newScreenX = viewWidth - (newMaxWidth - newPluginX); + if (newScreenX > rectViewX) { + newScreenX = rectViewX; + } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) { + newScreenX = viewWidth - (newMaxWidth - rectViewX); } mZoomCenterX = (oldScreenX * scale - newScreenX * mActualScale) / (scale - mActualScale); - int oldScreenY = contentToViewY(child.y) - mScrollY; - int newPluginY = (int) (child.y * scale) + getTitleHeight(); - int newPluginHeight = (int) (child.height * scale); - int newMaxHeight = (int) (mContentHeight * scale) + getTitleHeight(); - int newScreenY = (viewHeight - newPluginHeight) / 2; + int oldScreenY = contentToViewY(docY) - mScrollY; + int rectViewY = (int) (docY * scale) + getTitleHeight(); + int rectViewHeight = (int) (docHeight * scale); + int newMaxHeight = (int) (mContentHeight * scale) + + getTitleHeight(); + int newScreenY = (viewHeight - rectViewHeight) / 2; // pin the newY to the WebView - if (newScreenY > newPluginY) { - newScreenY = newPluginY; - } else if (newScreenY > (newMaxHeight - newPluginY - newPluginHeight)) { - newScreenY = viewHeight - (newMaxHeight - newPluginY); + if (newScreenY > rectViewY) { + newScreenY = rectViewY; + } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) { + newScreenY = viewHeight - (newMaxHeight - rectViewY); } mZoomCenterY = (oldScreenY * scale - newScreenY * mActualScale) / (scale - mActualScale); @@ -5810,7 +5828,7 @@ public class WebView extends AbsoluteLayout true); } else { mInZoomOverview = false; - centerPluginOnScreen(plugin); + centerFitRect(plugin.x, plugin.y, plugin.width, plugin.height); } return; } @@ -6627,6 +6645,12 @@ public class WebView extends AbsoluteLayout } break; + case CENTER_FIT_RECT: + Rect r = (Rect)msg.obj; + mInZoomOverview = false; + centerFitRect(r.left, r.top, r.width(), r.height()); + break; + default: super.handleMessage(msg); break; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 27c720888c5b..a27437845ae5 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2462,6 +2462,15 @@ final class WebViewCore { } } + // called by JNI + private void centerFitRect(int x, int y, int width, int height) { + if (mWebView == null) { + return; + } + mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT, + new Rect(x, y, x + width, y + height)).sendToTarget(); + } + private native void nativePause(); private native void nativeResume(); private native void nativeFreeMemory(); diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index 999b27d114d5..ee2fc121876d 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -1,3 +1,19 @@ +/* + * 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.widget; import android.content.Context; diff --git a/core/java/com/google/android/util/AbstractMessageParser.java b/core/java/com/google/android/util/AbstractMessageParser.java index 25f6b3347eb5..1871682ee063 100644 --- a/core/java/com/google/android/util/AbstractMessageParser.java +++ b/core/java/com/google/android/util/AbstractMessageParser.java @@ -1,5 +1,18 @@ -// Copyright 2007 The Android Open Source Project -// All Rights Reserved. +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.google.android.util; diff --git a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java index 02c763473286..da920c9a3e2e 100644 --- a/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java +++ b/core/tests/coretests/src/android/text/SpannableStringBuilderTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.text; import android.text.Spannable; diff --git a/core/tests/coretests/src/android/text/SpannableStringTest.java b/core/tests/coretests/src/android/text/SpannableStringTest.java index e9a328174723..8dd12146ea66 100644 --- a/core/tests/coretests/src/android/text/SpannableStringTest.java +++ b/core/tests/coretests/src/android/text/SpannableStringTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.text; import android.text.Spannable; diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java index 8e7891287d23..46be99d5c775 100644 --- a/core/tests/coretests/src/android/text/SpannableTest.java +++ b/core/tests/coretests/src/android/text/SpannableTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.text; import android.test.InstrumentationTestCase; diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java index 41947d7214da..30c81b0c7228 100644 --- a/core/tests/coretests/src/android/util/LogTest.java +++ b/core/tests/coretests/src/android/util/LogTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.util; import junit.framework.Assert; diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h index 23522bd0b882..b6176eb374d5 100644 --- a/include/media/stagefright/HTTPDataSource.h +++ b/include/media/stagefright/HTTPDataSource.h @@ -20,6 +20,7 @@ #include <media/stagefright/DataSource.h> #include <utils/String8.h> +#include <utils/threads.h> namespace android { @@ -35,6 +36,9 @@ public: const char *uri, const KeyedVector<String8, String8> *headers = NULL); + status_t connect(); + void disconnect(); + virtual status_t initCheck() const; virtual ssize_t readAt(off_t offset, void *data, size_t size); @@ -53,8 +57,21 @@ private: kBufferSize = 32 * 1024 }; + enum State { + DISCONNECTED, + CONNECTING, + CONNECTED + }; + + State mState; + mutable Mutex mStateLock; + String8 mHeaders; + String8 mStartingHost; + String8 mStartingPath; + int mStartingPort; + HTTPStream *mHttp; char *mHost; int mPort; @@ -67,11 +84,7 @@ private: bool mContentLengthValid; unsigned long long mContentLength; - status_t mInitCheck; - - void init( - const char *_host, int port, const char *_path, - const KeyedVector<String8, String8> *headers); + void init(const KeyedVector<String8, String8> *headers); ssize_t sendRangeRequest(size_t offset); void initHeaders(const KeyedVector<String8, String8> *overrides); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index e00ba47098d9..10b7be349103 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -26,6 +26,7 @@ #include <binder/IPCThreadState.h> #include <media/stagefright/AudioPlayer.h> +#include <media/stagefright/CachingDataSource.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/FileSource.h> #include <media/stagefright/MediaBuffer.h> @@ -318,6 +319,14 @@ void AwesomePlayer::reset() { } void AwesomePlayer::reset_l() { + if (mFlags & PREPARING) { + mFlags |= PREPARE_CANCELLED; + if (mConnectingDataSource != NULL) { + LOGI("interrupting the connection process"); + mConnectingDataSource->disconnect(); + } + } + while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } @@ -337,6 +346,12 @@ void AwesomePlayer::reset_l() { // If we did this later, audio would continue playing while we // shutdown the video-related resources and the player appear to // not be as responsive to a reset request. + if (mAudioPlayer == NULL && mAudioSource != NULL) { + // If we had an audio player, it would have effectively + // taken possession of the audio source and stopped it when + // _it_ is stopped. Otherwise this is still our responsibility. + mAudioSource->stop(); + } mAudioSource.clear(); if (mTimeSource != mAudioPlayer) { @@ -1039,8 +1054,29 @@ status_t AwesomePlayer::prepareAsync_l() { } status_t AwesomePlayer::finishSetDataSource_l() { - sp<DataSource> dataSource = - DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + sp<DataSource> dataSource; + + if (!strncasecmp("http://", mUri.string(), 7)) { + mConnectingDataSource = new HTTPDataSource(mUri, &mUriHeaders); + + mLock.unlock(); + status_t err = mConnectingDataSource->connect(); + mLock.lock(); + + if (err != OK) { + mConnectingDataSource.clear(); + + LOGI("mConnectingDataSource->connect() returned %d", err); + return err; + } + + dataSource = new CachingDataSource( + mConnectingDataSource, 32 * 1024, 20); + + mConnectingDataSource.clear(); + } else { + dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + } if (dataSource == NULL) { return UNKNOWN_ERROR; @@ -1067,7 +1103,7 @@ void AwesomePlayer::abortPrepare(status_t err) { } mPrepareResult = err; - mFlags &= ~PREPARING; + mFlags &= ~(PREPARING|PREPARE_CANCELLED); mAsyncPrepareEvent = NULL; mPreparedCondition.broadcast(); } @@ -1078,6 +1114,12 @@ void AwesomePlayer::onPrepareAsyncEvent() { { Mutex::Autolock autoLock(mLock); + if (mFlags & PREPARE_CANCELLED) { + LOGI("prepare was cancelled before doing anything"); + abortPrepare(UNKNOWN_ERROR); + return; + } + if (mUri.size() > 0) { status_t err = finishSetDataSource_l(); @@ -1109,7 +1151,19 @@ void AwesomePlayer::onPrepareAsyncEvent() { } if (prefetcher != NULL) { + { + Mutex::Autolock autoLock(mLock); + if (mFlags & PREPARE_CANCELLED) { + LOGI("prepare was cancelled before preparing the prefetcher"); + abortPrepare(UNKNOWN_ERROR); + return; + } + } + + LOGI("calling prefetcher->prepare()"); prefetcher->prepare(); + LOGV("prefetcher is done preparing"); + prefetcher.clear(); } @@ -1126,20 +1180,28 @@ void AwesomePlayer::onPrepareAsyncEvent() { } mPrepareResult = OK; - mFlags &= ~PREPARING; + mFlags &= ~(PREPARING|PREPARE_CANCELLED); mFlags |= PREPARED; mAsyncPrepareEvent = NULL; mPreparedCondition.broadcast(); } status_t AwesomePlayer::suspend() { - LOGI("suspend"); + LOGV("suspend"); Mutex::Autolock autoLock(mLock); if (mSuspensionState != NULL) { return INVALID_OPERATION; } + if (mFlags & PREPARING) { + mFlags |= PREPARE_CANCELLED; + if (mConnectingDataSource != NULL) { + LOGI("interrupting the connection process"); + mConnectingDataSource->disconnect(); + } + } + while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } @@ -1180,7 +1242,7 @@ status_t AwesomePlayer::suspend() { } status_t AwesomePlayer::resume() { - LOGI("resume"); + LOGV("resume"); Mutex::Autolock autoLock(mLock); if (mSuspensionState == NULL) { diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 8468a07dd6d8..284e3bc47020 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -101,8 +101,11 @@ sp<DataSource> DataSource::CreateFromURI( if (!strncasecmp("file://", uri, 7)) { source = new FileSource(uri + 7); } else if (!strncasecmp("http://", uri, 7)) { - source = new HTTPDataSource(uri, headers); - source = new CachingDataSource(source, 32 * 1024, 20); + sp<HTTPDataSource> httpSource = new HTTPDataSource(uri, headers); + if (httpSource->connect() != OK) { + return NULL; + } + source = new CachingDataSource(httpSource, 32 * 1024, 20); } else { // Assume it's a filename. source = new FileSource(uri); diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp index d79c1bd04a78..451fc2639ea9 100644 --- a/media/libstagefright/HTTPDataSource.cpp +++ b/media/libstagefright/HTTPDataSource.cpp @@ -126,41 +126,71 @@ HTTPDataSource::HTTPDataSource( host = string(host, 0, colon - host.c_str()); } - init(host.c_str(), port, path.c_str(), headers); + mStartingHost = host.c_str(); + mStartingPath = path.c_str(); + mStartingPort = port; + + init(headers); } HTTPDataSource::HTTPDataSource( const char *_host, int port, const char *_path, const KeyedVector<String8, String8> *headers) { - init(_host, port, _path, headers); + mStartingHost = _host; + mStartingPath = _path; + mStartingPort = port; + + init(headers); } -void HTTPDataSource::init( - const char *_host, int port, const char *_path, - const KeyedVector<String8, String8> *headers) { +void HTTPDataSource::init(const KeyedVector<String8, String8> *headers) { + mState = DISCONNECTED; mHttp = new HTTPStream; + mHost = NULL; mPort = 0; - mPath = NULL, + mPath = NULL; + + initHeaders(headers); + mBuffer = malloc(kBufferSize); mBufferLength = 0; mBufferOffset = 0; - mContentLengthValid = false; +} - initHeaders(headers); +status_t HTTPDataSource::connect() { + { + Mutex::Autolock autoLock(mStateLock); - string host = _host; - string path = _path; + if (mState != DISCONNECTED) { + return ERROR_ALREADY_CONNECTED; + } + + mState = CONNECTING; + } + + mContentLengthValid = false; + + string host = mStartingHost.string(); + string path = mStartingPath.string(); + int port = mStartingPort; LOGI("Connecting to host '%s', port %d, path '%s'", host.c_str(), port, path.c_str()); int numRedirectsRemaining = 5; do { - mInitCheck = mHttp->connect(host.c_str(), port); + status_t err = mHttp->connect(host.c_str(), port); - if (mInitCheck != OK) { - return; + if (err != OK) { + Mutex::Autolock autoLock(mStateLock); + + if (mState != CONNECTING) { + LOGV("connect() cancelled"); + } + mState = DISCONNECTED; + + return err; } } while (PerformRedirectIfNecessary(mHttp, mHeaders, &host, &path, &port) && numRedirectsRemaining-- > 0); @@ -175,17 +205,44 @@ void HTTPDataSource::init( mHost = strdup(host.c_str()); mPort = port; mPath = strdup(path.c_str()); + + Mutex::Autolock autoLock(mStateLock); + + if (mState != CONNECTING) { + // disconnect was called when we had just successfully connected. + LOGV("connect() cancelled (we had just succeeded connecting)"); + + mHttp->disconnect(); + return UNKNOWN_ERROR; + } + + mState = CONNECTED; + return OK; +} + +void HTTPDataSource::disconnect() { + Mutex::Autolock autoLock(mStateLock); + + if (mState == CONNECTING || mState == CONNECTED) { + mHttp->disconnect(); + mState = DISCONNECTED; + } } status_t HTTPDataSource::initCheck() const { - return mInitCheck; + Mutex::Autolock autoLock(mStateLock); + + return (mState == CONNECTED) ? (status_t)OK : ERROR_NOT_CONNECTED; } status_t HTTPDataSource::getSize(off_t *size) { *size = 0; - if (mInitCheck != OK) { - return mInitCheck; + { + Mutex::Autolock autoLock(mStateLock); + if (mState != CONNECTED) { + return ERROR_NOT_CONNECTED; + } } if (!mContentLengthValid) { @@ -198,7 +255,10 @@ status_t HTTPDataSource::getSize(off_t *size) { } HTTPDataSource::~HTTPDataSource() { - mHttp->disconnect(); + disconnect(); + + delete mHttp; + mHttp = NULL; free(mBuffer); mBuffer = NULL; @@ -212,9 +272,6 @@ HTTPDataSource::~HTTPDataSource() { free(mHost); mHost = NULL; } - - delete mHttp; - mHttp = NULL; } ssize_t HTTPDataSource::sendRangeRequest(size_t offset) { @@ -271,6 +328,13 @@ ssize_t HTTPDataSource::sendRangeRequest(size_t offset) { ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { LOGV("readAt %ld, size %d", offset, size); + { + Mutex::Autolock autoLock(mStateLock); + if (mState != CONNECTED) { + return ERROR_NOT_CONNECTED; + } + } + if (offset >= mBufferOffset && offset < (off_t)(mBufferOffset + mBufferLength)) { size_t num_bytes_available = mBufferLength - (offset - mBufferOffset); @@ -304,7 +368,7 @@ ssize_t HTTPDataSource::readAt(off_t offset, void *data, size_t size) { mBufferOffset = offset; if (mContentLengthValid - && mBufferOffset + contentLength >= mContentLength) { + && mBufferOffset + contentLength >= (off_t)mContentLength) { // If we never triggered a range request but know the content length, // make sure to not read more data than there could be, otherwise // we'd block indefinitely if the server doesn't close the connection. diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index 2c5da686b50e..66eadf68f6a0 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -44,12 +44,19 @@ HTTPStream::~HTTPStream() { } status_t HTTPStream::connect(const char *server, int port) { + Mutex::Autolock autoLock(mLock); + status_t err = OK; if (mState == CONNECTED) { return ERROR_ALREADY_CONNECTED; } + struct hostent *ent = gethostbyname(server); + if (ent == NULL) { + return ERROR_UNKNOWN_HOST; + } + CHECK_EQ(mSocket, -1); mSocket = socket(AF_INET, SOCK_STREAM, 0); @@ -57,11 +64,11 @@ status_t HTTPStream::connect(const char *server, int port) { return UNKNOWN_ERROR; } - struct hostent *ent = gethostbyname(server); - if (ent == NULL) { - err = ERROR_UNKNOWN_HOST; - goto exit1; - } + mState = CONNECTING; + + int s = mSocket; + + mLock.unlock(); struct sockaddr_in addr; addr.sin_family = AF_INET; @@ -69,24 +76,31 @@ status_t HTTPStream::connect(const char *server, int port) { addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr; memset(addr.sin_zero, 0, sizeof(addr.sin_zero)); - if (::connect(mSocket, (const struct sockaddr *)&addr, sizeof(addr)) < 0) { - err = ERROR_CANNOT_CONNECT; - goto exit1; + int res = ::connect(s, (const struct sockaddr *)&addr, sizeof(addr)); + + mLock.lock(); + + if (mState != CONNECTING) { + return UNKNOWN_ERROR; } - mState = CONNECTED; + if (res < 0) { + close(mSocket); + mSocket = -1; - return OK; + mState = READY; + return UNKNOWN_ERROR; + } -exit1: - close(mSocket); - mSocket = -1; + mState = CONNECTED; - return err; + return OK; } status_t HTTPStream::disconnect() { - if (mState != CONNECTED) { + Mutex::Autolock autoLock(mLock); + + if (mState != CONNECTED && mState != CONNECTING) { return ERROR_NOT_CONNECTED; } diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index c0cf97cd628a..4679207691f1 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -23,7 +23,6 @@ #include <media/stagefright/ColorConverter.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/FileSource.h> -#include <media/stagefright/HTTPDataSource.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MetaData.h> diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 7106524afbb5..32c28c1ddab2 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -22,6 +22,7 @@ #include <media/MediaPlayerInterface.h> #include <media/stagefright/DataSource.h> +#include <media/stagefright/HTTPDataSource.h> #include <media/stagefright/OMXClient.h> #include <utils/threads.h> @@ -87,12 +88,13 @@ private: friend struct AwesomeEvent; enum Flags { - PLAYING = 1, - LOOPING = 2, - FIRST_FRAME = 4, - PREPARING = 8, - PREPARED = 16, - AT_EOS = 32, + PLAYING = 1, + LOOPING = 2, + FIRST_FRAME = 4, + PREPARING = 8, + PREPARED = 16, + AT_EOS = 32, + PREPARE_CANCELLED = 64, }; mutable Mutex mLock; @@ -160,6 +162,7 @@ private: MediaBuffer *mVideoBuffer; sp<Prefetcher> mPrefetcher; + sp<HTTPDataSource> mConnectingDataSource; struct SuspensionState { String8 mUri; diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 43ef614d71a6..5d638f386d45 100644 --- a/media/libstagefright/include/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -24,6 +24,7 @@ #include <media/stagefright/MediaErrors.h> #include <utils/KeyedVector.h> +#include <utils/threads.h> namespace android { @@ -54,10 +55,12 @@ public: private: enum State { READY, + CONNECTING, CONNECTED }; State mState; + Mutex mLock; int mSocket; KeyedVector<string, string> mHeaders; diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index ff8757d52851..13cb46046b58 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -19,6 +19,8 @@ #include <utils/Log.h> #include <dlfcn.h> +#include <linux/prctl.h> +#include <sys/resource.h> #include "../include/OMX.h" #include "OMXRenderer.h" @@ -29,6 +31,7 @@ #include <binder/IMemory.h> #include <media/stagefright/MediaDebug.h> #include <media/stagefright/VideoRenderer.h> +#include <utils/threads.h> #include "OMXMaster.h" @@ -91,6 +94,7 @@ OMX::CallbackDispatcher::~CallbackDispatcher() { void OMX::CallbackDispatcher::post(const omx_message &msg) { Mutex::Autolock autoLock(mLock); + mQueue.push_back(msg); mQueueChanged.signal(); } @@ -112,6 +116,9 @@ void *OMX::CallbackDispatcher::ThreadWrapper(void *me) { } void OMX::CallbackDispatcher::threadEntry() { + setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); + prctl(PR_SET_NAME, (unsigned long)"OMXCallbackDisp", 0, 0, 0); + for (;;) { omx_message msg; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java index ddf5e0bfaf0e..8489a67052f7 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMimeTest.java @@ -49,7 +49,7 @@ public class MediaMimeTest extends ActivityInstrumentationTestCase2<MediaFramewo private final String TAG = "MediaMimeTest"; private Context mContext; private final String MP3_FILE = "/sdcard/media_api/music/SHORTMP3.mp3"; - private final String MEDIA_PLAYBACK_NAME = "com.android.music.MediaPlaybackActivity"; + private final String MEDIA_PLAYBACK_NAME = "com.android.music.MediaPlaybackActivityStarter"; public MediaMimeTest() { super("com.android.mediaframeworktest", MediaFrameworkTest.class); @@ -77,7 +77,7 @@ public class MediaMimeTest extends ActivityInstrumentationTestCase2<MediaFramewo assertMediaPlaybackActivityHandles("audio/mp3"); } - @MediumTest + @Suppress // Checks the MediaPlaybackActivity handles audio/*. public void testCheckMediaPlaybackHandlesAudio() throws Exception { assertMediaPlaybackActivityHandles("audio/*"); @@ -86,7 +86,7 @@ public class MediaMimeTest extends ActivityInstrumentationTestCase2<MediaFramewo // TODO: temporarily remove from medium suite because it hangs whole suite // @MediumTest // Checks the MediaPlaybackActivity handles application/itunes. Some servers - // set the Content-type header to application/iTunes (with capital T, but + // set the Content-type hadb ieader to application/iTunes (with capital T, but // the download manager downcasts it) for their MP3 podcasts. This is non // standard but we try to support it anyway. // See bug 1401491 diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index a3bbb7447f7a..8202efd9b211 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -395,10 +395,10 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi Thread.sleep(10000); mEndPid = getMediaserverPid(); int memDiff = mEndMemory - startMemory; - if (memDiff < 0) + if (memDiff < 0) { memDiff = 0; - else - output.write("The total diff = " + memDiff); + } + output.write("The total diff = " + memDiff); output.write("\n\n"); // mediaserver crash if (startPid != mEndPid) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index fc20d969238e..81b8d40b0a37 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -81,8 +81,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private List mNetRequestersPids[]; - private WifiWatchdogService mWifiWatchdogService; - // priority order of the nettrackers // (excluding dynamically set mNetworkPreference) // TODO - move mNetworkTypePreference into this @@ -298,11 +296,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { WifiStateTracker wst = new WifiStateTracker(context, mHandler); WifiService wifiService = new WifiService(context, wst); ServiceManager.addService(Context.WIFI_SERVICE, wifiService); + wifiService.startWifi(); mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; wst.startMonitoring(); - // Constructing this starts it too - mWifiWatchdogService = new WifiWatchdogService(context, wst); break; case ConnectivityManager.TYPE_MOBILE: mNetTrackers[netType] = new MobileDataStateTracker(context, mHandler, diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index fc6bfcd72a90..d72416d8206b 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -2205,32 +2205,28 @@ class PowerManagerService extends IPowerManager.Stub if (mHandler == null || !ActivityManagerNative.isSystemReady()) { throw new IllegalStateException("Too early to call reboot()"); } - + final String finalReason = reason; Runnable runnable = new Runnable() { public void run() { synchronized (this) { ShutdownThread.reboot(mContext, finalReason, false); - // if we get here we failed - notify(); } } }; - + // ShutdownThread must run on a looper capable of displaying the UI. mHandler.post(runnable); - // block until we reboot or fail. - // throw an exception if we failed to reboot + // PowerManager.reboot() is documented not to return so just wait for the inevitable. synchronized (runnable) { - try { - runnable.wait(); - } catch (InterruptedException e) { + while (true) { + try { + runnable.wait(); + } catch (InterruptedException e) { + } } } - - // if we get here we failed - throw new IllegalStateException("unable to reboot!"); } /** diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index 8103d7cee64a..84790caf7880 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -52,6 +52,7 @@ import android.util.Slog; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.Iterator; import com.android.internal.R; import com.android.internal.app.DisableCarModeActivity; @@ -76,10 +77,10 @@ class UiModeManagerService extends IUiModeManager.Stub { private final Context mContext; final Object mLock = new Object(); - + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; - + private int mNightMode = UiModeManager.MODE_NIGHT_NO; private boolean mCarModeEnabled = false; private boolean mCharging = false; @@ -89,10 +90,10 @@ class UiModeManagerService extends IUiModeManager.Stub { private boolean mComputedNightMode; private int mCurUiMode = 0; private int mSetUiMode = 0; - + private boolean mHoldingConfiguration = false; private Configuration mConfiguration = new Configuration(); - + private boolean mSystemReady; private NotificationManager mNotificationManager; @@ -138,7 +139,7 @@ class UiModeManagerService extends IUiModeManager.Stub { Slog.w(TAG, e.getCause()); } } - + if (mHoldingConfiguration) { mHoldingConfiguration = false; updateConfigurationLocked(); @@ -255,7 +256,7 @@ class UiModeManagerService extends IUiModeManager.Stub { mContext = context; ServiceManager.addService(Context.UI_MODE_SERVICE, this); - + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); mLocationManager = @@ -304,7 +305,7 @@ class UiModeManagerService extends IUiModeManager.Stub { return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; } } - + public void setNightMode(int mode) throws RemoteException { synchronized (mLock) { switch (mode) { @@ -318,18 +319,18 @@ class UiModeManagerService extends IUiModeManager.Stub { if (!isDoingNightMode()) { return; } - + if (mNightMode != mode) { mNightMode = mode; updateLocked(); } } } - + public int getNightMode() throws RemoteException { return mNightMode; } - + void systemReady() { synchronized (mLock) { mSystemReady = true; @@ -342,7 +343,7 @@ class UiModeManagerService extends IUiModeManager.Stub { boolean isDoingNightMode() { return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; } - + void setCarModeLocked(boolean enabled) { if (mCarModeEnabled != enabled) { mCarModeEnabled = enabled; @@ -399,12 +400,12 @@ class UiModeManagerService extends IUiModeManager.Stub { uiMode = Configuration.UI_MODE_TYPE_NORMAL | Configuration.UI_MODE_NIGHT_NO; } - + mCurUiMode = uiMode; - + if (!mHoldingConfiguration && uiMode != mSetUiMode) { mSetUiMode = uiMode; - + try { final IActivityManager am = ActivityManagerNative.getDefault(); mConfiguration.uiMode = uiMode; @@ -414,10 +415,10 @@ class UiModeManagerService extends IUiModeManager.Stub { } } } - + final void updateLocked() { long ident = Binder.clearCallingIdentity(); - + try { String action = null; String oldAction = null; @@ -426,11 +427,11 @@ class UiModeManagerService extends IUiModeManager.Stub { } else if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_DESK) { oldAction = UiModeManager.ACTION_EXIT_DESK_MODE; } - + if (mCarModeEnabled) { if (mLastBroadcastState != Intent.EXTRA_DOCK_STATE_CAR) { adjustStatusBarCarModeLocked(); - + if (oldAction != null) { mContext.sendBroadcast(new Intent(oldAction)); } @@ -449,11 +450,11 @@ class UiModeManagerService extends IUiModeManager.Stub { if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { adjustStatusBarCarModeLocked(); } - + mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; action = oldAction; } - + if (action != null) { // Send the ordered broadcast; the result receiver will receive after all // broadcasts have been sent. If any broadcast receiver changes the result @@ -468,7 +469,7 @@ class UiModeManagerService extends IUiModeManager.Stub { // the broacast and started the home activity. mHoldingConfiguration = true; } - + updateConfigurationLocked(); // keep screen on when charging and in car mode @@ -609,14 +610,18 @@ class UiModeManagerService extends IUiModeManager.Stub { } private void retrieveLocation() { - Location location; - Criteria criteria = new Criteria(); - criteria.setSpeedRequired(false); - criteria.setAltitudeRequired(false); - criteria.setBearingRequired(false); - criteria.setAccuracy(Criteria.ACCURACY_FINE); - final String bestProvider = mLocationManager.getBestProvider(criteria, true); - location = mLocationManager.getLastKnownLocation(bestProvider); + Location location = null; + final Iterator<String> providers = + mLocationManager.getProviders(new Criteria(), true).iterator(); + while (providers.hasNext()) { + final Location lastKnownLocation = + mLocationManager.getLastKnownLocation(providers.next()); + // pick the most recent location + if (location == null || (lastKnownLocation != null && + location.getTime() < lastKnownLocation.getTime())) { + location = lastKnownLocation; + } + } // In the case there is no location available (e.g. GPS fix or network location // is not available yet), the longitude of the location is estimated using the timezone, // latitude and accuracy are set to get a good average. @@ -684,18 +689,18 @@ class UiModeManagerService extends IUiModeManager.Stub { mComputedNightMode = nightMode; } - + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { - + pw.println("Permission Denial: can't dump uimode service from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } - + synchronized (mLock) { pw.println("Current UI Mode Service state:"); pw.print(" mDockState="); pw.print(mDockState); diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 3f64b25796df..be1d1c4cf064 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -828,7 +828,13 @@ public class Watchdog extends Thread { ArrayList pids = new ArrayList(); pids.add(Process.myPid()); + if (mPhonePid > 0) pids.add(mPhonePid); File stack = ActivityManagerService.dumpStackTraces(pids); + + // Give some extra time to make sure the stack traces get written. + // The system's been hanging for a minute, another second or two won't hurt much. + SystemClock.sleep(2000); + mActivity.addErrorToDropBox("watchdog", null, null, null, name, null, stack, null); // Only kill the process if the debugger is not attached. diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 248f5798ee16..97a43294f967 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -124,6 +124,7 @@ public class WifiService extends IWifiManager.Stub { private INetworkManagementService nwService; ConnectivityManager mCm; + private WifiWatchdogService mWifiWatchdogService = null; private String[] mWifiRegexs; /** @@ -217,8 +218,6 @@ public class WifiService extends IWifiManager.Stub { mWifiStateTracker.setWifiState(WIFI_STATE_DISABLED); mWifiApState = WIFI_AP_STATE_DISABLED; - boolean wifiEnabled = getPersistedWifiEnabled(); - boolean wifiAPEnabled = wifiEnabled ? false : getPersistedWifiApEnabled(); mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); @@ -240,9 +239,6 @@ public class WifiService extends IWifiManager.Stub { } ); - Slog.i(TAG, "WifiService starting up with Wi-Fi " + - (wifiEnabled ? "enabled" : "disabled")); - mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -267,7 +263,17 @@ public class WifiService extends IWifiManager.Stub { } },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); + } + /** + * Check if Wi-Fi needs to be enabled and start + * if needed + */ + public void startWifi() { + boolean wifiEnabled = getPersistedWifiEnabled(); + boolean wifiAPEnabled = wifiEnabled ? false : getPersistedWifiApEnabled(); + Slog.i(TAG, "WifiService starting up with Wi-Fi " + + (wifiEnabled ? "enabled" : "disabled")); setWifiEnabledBlocking(wifiEnabled, false, Process.myUid()); setWifiApEnabledBlocking(wifiAPEnabled, true, Process.myUid(), null); } @@ -446,6 +452,7 @@ public class WifiService extends IWifiManager.Stub { registerForBroadcasts(); mWifiStateTracker.startEventLoop(); + } else { mContext.unregisterReceiver(mReceiver); @@ -1803,6 +1810,9 @@ public class WifiService extends IWifiManager.Stub { switch (msg.what) { case MESSAGE_ENABLE_WIFI: + if (mWifiWatchdogService == null) { + mWifiWatchdogService = new WifiWatchdogService(mContext, mWifiStateTracker); + } setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2); sWakeLock.release(); break; @@ -1821,6 +1831,10 @@ public class WifiService extends IWifiManager.Stub { // a non-zero msg.arg1 value means the "enabled" setting // should be persisted setWifiEnabledBlocking(false, msg.arg1 == 1, msg.arg2); + if (mWifiWatchdogService != null) { + mWifiWatchdogService.quit(); + mWifiWatchdogService = null; + } sWakeLock.release(); break; diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index e50b317a9992..e2c523ddfd4e 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -89,6 +89,8 @@ public class WifiWatchdogService { */ private WifiWatchdogHandler mHandler; + private ContentObserver mContentObserver; + /** * The current watchdog state. Only written from the main thread! */ @@ -132,7 +134,7 @@ public class WifiWatchdogService { ContentResolver contentResolver = mContext.getContentResolver(); contentResolver.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.WIFI_WATCHDOG_ON), false, - new ContentObserver(mHandler) { + mContentObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { if (isWatchdogEnabled()) { @@ -272,6 +274,16 @@ public class WifiWatchdogService { } /** + * Unregister broadcasts and quit the watchdog thread + */ + public void quit() { + unregisterForWifiBroadcasts(); + mContext.getContentResolver().unregisterContentObserver(mContentObserver); + mHandler.removeAllActions(); + mHandler.getLooper().quit(); + } + + /** * Waits for the main watchdog thread to create the handler. */ private void waitForHandlerCreation() { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index f6289ae3abb6..b6064e816086 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -111,6 +111,7 @@ import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintWriter; import java.lang.IllegalStateException; import java.lang.ref.WeakReference; @@ -4267,6 +4268,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean willActivityBeVisible(IBinder token) { + synchronized(this) { + int i; + for (i=mHistory.size()-1; i>=0; i--) { + HistoryRecord r = (HistoryRecord)mHistory.get(i); + if (r == token) { + return true; + } + if (r.fullscreen && !r.finishing) { + return false; + } + } + return true; + } + } + public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim) { synchronized(this) { @@ -4708,7 +4725,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final void appNotRespondingLocked(ProcessRecord app, HistoryRecord activity, HistoryRecord parent, final String annotation) { - if (app.notResponding || app.crashing) { + // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. + if (mShuttingDown || app.notResponding || app.crashing) { return; } @@ -8959,77 +8977,122 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @param crashInfo giving an application stack trace, null if absent */ public void addErrorToDropBox(String eventType, - ProcessRecord process, HistoryRecord activity, HistoryRecord parent, - String subject, String report, File logFile, - ApplicationErrorReport.CrashInfo crashInfo) { + ProcessRecord process, HistoryRecord activity, HistoryRecord parent, String subject, + final String report, final File logFile, + final ApplicationErrorReport.CrashInfo crashInfo) { // NOTE -- this must never acquire the ActivityManagerService lock, // otherwise the watchdog may be prevented from resetting the system. - String dropboxTag; + String prefix; if (process == null || process.pid == MY_PID) { - dropboxTag = "system_server_" + eventType; + prefix = "system_server_"; } else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - dropboxTag = "system_app_" + eventType; + prefix = "system_app_"; } else { - dropboxTag = "data_app_" + eventType; + prefix = "data_app_"; } - DropBoxManager dbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); - if (dbox != null && dbox.isTagEnabled(dropboxTag)) { - StringBuilder sb = new StringBuilder(1024); - if (process == null || process.pid == MY_PID) { - sb.append("Process: system_server\n"); - } else { - sb.append("Process: ").append(process.processName).append("\n"); - } - if (process != null) { - int flags = process.info.flags; - IPackageManager pm = ActivityThread.getPackageManager(); - sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); - for (String pkg : process.pkgList) { - sb.append("Package: ").append(pkg); - try { - PackageInfo pi = pm.getPackageInfo(pkg, 0); - if (pi != null) { - sb.append(" v").append(pi.versionCode); - if (pi.versionName != null) { - sb.append(" (").append(pi.versionName).append(")"); - } + final String dropboxTag = prefix + eventType; + final DropBoxManager dbox = (DropBoxManager) + mContext.getSystemService(Context.DROPBOX_SERVICE); + + // Exit early if the dropbox isn't configured to accept this report type. + if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return; + + final StringBuilder sb = new StringBuilder(1024); + if (process == null || process.pid == MY_PID) { + sb.append("Process: system_server\n"); + } else { + sb.append("Process: ").append(process.processName).append("\n"); + } + if (process != null) { + int flags = process.info.flags; + IPackageManager pm = ActivityThread.getPackageManager(); + sb.append("Flags: 0x").append(Integer.toString(flags, 16)).append("\n"); + for (String pkg : process.pkgList) { + sb.append("Package: ").append(pkg); + try { + PackageInfo pi = pm.getPackageInfo(pkg, 0); + if (pi != null) { + sb.append(" v").append(pi.versionCode); + if (pi.versionName != null) { + sb.append(" (").append(pi.versionName).append(")"); } - } catch (RemoteException e) { - Slog.e(TAG, "Error getting package info: " + pkg, e); } - sb.append("\n"); + } catch (RemoteException e) { + Slog.e(TAG, "Error getting package info: " + pkg, e); } + sb.append("\n"); } - if (activity != null) { - sb.append("Activity: ").append(activity.shortComponentName).append("\n"); - } - if (parent != null && parent.app != null && parent.app.pid != process.pid) { - sb.append("Parent-Process: ").append(parent.app.processName).append("\n"); - } - if (parent != null && parent != activity) { - sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n"); - } - if (subject != null) { - sb.append("Subject: ").append(subject).append("\n"); - } - sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); - sb.append("\n"); - if (report != null) { - sb.append(report); - } - if (logFile != null) { - try { - sb.append(FileUtils.readTextFile(logFile, 128 * 1024, "\n\n[[TRUNCATED]]")); - } catch (IOException e) { - Slog.e(TAG, "Error reading " + logFile, e); + } + if (activity != null) { + sb.append("Activity: ").append(activity.shortComponentName).append("\n"); + } + if (parent != null && parent.app != null && parent.app.pid != process.pid) { + sb.append("Parent-Process: ").append(parent.app.processName).append("\n"); + } + if (parent != null && parent != activity) { + sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n"); + } + if (subject != null) { + sb.append("Subject: ").append(subject).append("\n"); + } + sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); + sb.append("\n"); + + // Do the rest in a worker thread to avoid blocking the caller on I/O + // (After this point, we shouldn't access AMS internal data structures.) + Thread worker = new Thread("Error dump: " + dropboxTag) { + @Override + public void run() { + if (report != null) { + sb.append(report); } + if (logFile != null) { + try { + sb.append(FileUtils.readTextFile(logFile, 128 * 1024, "\n\n[[TRUNCATED]]")); + } catch (IOException e) { + Slog.e(TAG, "Error reading " + logFile, e); + } + } + if (crashInfo != null && crashInfo.stackTrace != null) { + sb.append(crashInfo.stackTrace); + } + + String setting = Settings.Secure.ERROR_LOGCAT_PREFIX + dropboxTag; + int lines = Settings.Secure.getInt(mContext.getContentResolver(), setting, 0); + if (lines > 0) { + sb.append("\n"); + + // Merge several logcat streams, and take the last N lines + InputStreamReader input = null; + try { + java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat", + "-v", "time", "-b", "events", "-b", "system", "-b", "main", + "-t", String.valueOf(lines)).redirectErrorStream(true).start(); + + try { logcat.getOutputStream().close(); } catch (IOException e) {} + try { logcat.getErrorStream().close(); } catch (IOException e) {} + input = new InputStreamReader(logcat.getInputStream()); + + int num; + char[] buf = new char[8192]; + while ((num = input.read(buf)) > 0) sb.append(buf, 0, num); + } catch (IOException e) { + Slog.e(TAG, "Error running logcat", e); + } finally { + if (input != null) try { input.close(); } catch (IOException e) {} + } + } + + dbox.addText(dropboxTag, sb.toString()); } - if (crashInfo != null && crashInfo.stackTrace != null) { - sb.append(crashInfo.stackTrace); - } - dbox.addText(dropboxTag, sb.toString()); + }; + + if (process == null || process.pid == MY_PID) { + worker.run(); // We may be about to die -- need to run this synchronously + } else { + worker.start(); } } diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 25f123c9be69..ccb5fc6346aa 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -123,17 +123,21 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper); mTetherMasterSM.start(); - // TODO - remove this hack after real USB connections are detected. + mStateReceiver = new StateReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(Intent.ACTION_BOOT_COMPLETED); + mContext.registerReceiver(mStateReceiver, filter); + + filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_SHARED); filter.addAction(Intent.ACTION_MEDIA_UNSHARED); + filter.addDataScheme("file"); + mContext.registerReceiver(mStateReceiver, filter); + mUsbMassStorageOff = !Environment.MEDIA_SHARED.equals( Environment.getExternalStorageState()); - mStateReceiver = new StateReceiver(); - mContext.registerReceiver(mStateReceiver, filter); mDhcpRange = context.getResources().getStringArray( com.android.internal.R.array.config_tether_dhcp_range); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index 08946d249356..188145b33fcb 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -592,8 +592,11 @@ public class CdmaConnection extends Connection { if (!isIncoming) { // outgoing calls only processNextPostDialChar(); + } else { + // Only release wake lock for incoming calls, for outgoing calls the wake lock + // will be released after any pause-dial is completed + releaseWakeLock(); } - releaseWakeLock(); } private void @@ -688,6 +691,7 @@ public class CdmaConnection extends Connection { Registrant postDialHandler; if (postDialState == PostDialState.CANCELLED) { + releaseWakeLock(); //Log.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail"); return; } @@ -696,6 +700,9 @@ public class CdmaConnection extends Connection { postDialString.length() <= nextPostDialChar) { setPostDialState(PostDialState.COMPLETE); + // We were holding a wake lock until pause-dial was complete, so give it up now + releaseWakeLock(); + // notifyMessage.arg1 is 0 on complete c = 0; } else { @@ -770,19 +777,24 @@ public class CdmaConnection extends Connection { } /** - * Set post dial state and acquire wake lock while switching to "started" - * state, the wake lock will be released if state switches out of "started" + * Set post dial state and acquire wake lock while switching to "started" or "wait" + * state, the wake lock will be released if state switches out of "started" or "wait" * state or after WAKE_LOCK_TIMEOUT_MILLIS. * @param s new PostDialState */ private void setPostDialState(PostDialState s) { - if (postDialState != PostDialState.STARTED - && s == PostDialState.STARTED) { - acquireWakeLock(); - Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); - h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); - } else if (postDialState == PostDialState.STARTED - && s != PostDialState.STARTED) { + if (s == PostDialState.STARTED || + s == PostDialState.PAUSE) { + synchronized (mPartialWakeLock) { + if (mPartialWakeLock.isHeld()) { + h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); + } else { + acquireWakeLock(); + } + Message msg = h.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT); + h.sendMessageDelayed(msg, WAKE_LOCK_TIMEOUT_MILLIS); + } + } else { h.removeMessages(EVENT_WAKE_LOCK_TIMEOUT); releaseWakeLock(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/EriInfo.java b/telephony/java/com/android/internal/telephony/cdma/EriInfo.java index 5c8e23e81b4c..3e5d37e39b59 100644 --- a/telephony/java/com/android/internal/telephony/cdma/EriInfo.java +++ b/telephony/java/com/android/internal/telephony/cdma/EriInfo.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2009 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 diff --git a/telephony/java/com/android/internal/telephony/cdma/EriManager.java b/telephony/java/com/android/internal/telephony/cdma/EriManager.java index 37c1d55eec47..1bcc90a07063 100644 --- a/telephony/java/com/android/internal/telephony/cdma/EriManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/EriManager.java @@ -1,4 +1,6 @@ /* + * Copyright (C) 2009 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 diff --git a/test-runner/src/android/test/BundlePrinter.java b/test-runner/src/android/test/BundlePrinter.java index 96213e735bac..3c473798ab6a 100644 --- a/test-runner/src/android/test/BundlePrinter.java +++ b/test-runner/src/android/test/BundlePrinter.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.test; import java.io.PrintStream; diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java index f0935989b545..b483b825fdef 100644 --- a/test-runner/src/android/test/IsolatedContext.java +++ b/test-runner/src/android/test/IsolatedContext.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.test; import com.google.android.collect.Lists; diff --git a/test-runner/src/android/test/ProviderTestCase.java b/test-runner/src/android/test/ProviderTestCase.java index 668e9f7adb11..e1172cf21ff7 100644 --- a/test-runner/src/android/test/ProviderTestCase.java +++ b/test-runner/src/android/test/ProviderTestCase.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.test; import android.content.ContentProvider; diff --git a/test-runner/src/android/test/ProviderTestCase2.java b/test-runner/src/android/test/ProviderTestCase2.java index f3655fc781dd..28ecee51af0a 100644 --- a/test-runner/src/android/test/ProviderTestCase2.java +++ b/test-runner/src/android/test/ProviderTestCase2.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.test; import android.content.ContentProvider; diff --git a/test-runner/src/android/test/RenamingDelegatingContext.java b/test-runner/src/android/test/RenamingDelegatingContext.java index 0ea43ab5abe1..973b9f285f95 100644 --- a/test-runner/src/android/test/RenamingDelegatingContext.java +++ b/test-runner/src/android/test/RenamingDelegatingContext.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.test; import com.google.android.collect.Sets; @@ -230,4 +246,4 @@ public class RenamingDelegatingContext extends ContextWrapper { // } // return files.toArray(new String[]{}); // } -}
\ No newline at end of file +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index eecd00aa6eea..e3486a0ec92b 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -24,9 +24,8 @@ public class FileFilter { private static final String LOGTAG = "FileFilter"; // Returns whether we should ignore this test and skip running it. - // Currently we use this only for tests that crash the browser. - // TODO: Once these crashes are fixed, we should probably eliminate this - // method, as no test should crash. + // Currently we use this only for tests that crash or hang DumpRenderTree. + // TODO: Fix these and eliminate this method. public static boolean ignoreTest(String file) { for (int i = 0; i < ignoreTestList.length; i++) { if (file.endsWith(ignoreTestList[i])) { @@ -74,6 +73,8 @@ public class FileFilter { static final String[] ignoreTestList = { "fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM + "fast/regex/test1.html", // Causes DumpRenderTree to hang with V8 + "fast/regex/slow.html", // Causes DumpRenderTree to hang with V8 "editing/selection/move-left-right.html" // Causes DumpRenderTree to hang }; diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index f2414dd85487..3c471ca76107 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -359,7 +359,7 @@ static status_t do_9patch(const char* imageName, image_info* image) const char* errorMsg = NULL; int errorPixel = -1; - const char* errorEdge = ""; + const char* errorEdge = NULL; int colorIndex = 0; @@ -463,6 +463,14 @@ static status_t do_9patch(const char* imageName, image_info* image) if (yDivs[numYDivs - 1] == H) { numRows--; } + + // Make sure the amount of rows and columns will fit in the number of + // colors we can use in the 9-patch format. + if (numRows * numCols > 0x7F) { + errorMsg = "Too many rows and columns in 9-patch perimeter"; + goto getout; + } + numColors = numRows * numCols; image->info9Patch.numColors = numColors; image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t)); @@ -533,12 +541,14 @@ getout: fprintf(stderr, "ERROR: 9-patch image %s malformed.\n" " %s.\n", imageName, errorMsg); - if (errorPixel >= 0) { - fprintf(stderr, - " Found at pixel #%d along %s edge.\n", errorPixel, errorEdge); - } else { - fprintf(stderr, - " Found along %s edge.\n", errorEdge); + if (errorEdge != NULL) { + if (errorPixel >= 0) { + fprintf(stderr, + " Found at pixel #%d along %s edge.\n", errorPixel, errorEdge); + } else { + fprintf(stderr, + " Found along %s edge.\n", errorEdge); + } } return UNKNOWN_ERROR; } @@ -613,7 +623,7 @@ static void dump_image(int w, int h, png_bytepp rows, int color_type) } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { bpp = 2; } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - // We use a padding byte even when there is no alpha + // We use a padding byte even when there is no alpha bpp = 4; } else { printf("Unknown color type %d.\n", color_type); |