diff options
101 files changed, 3212 insertions, 939 deletions
@@ -126,6 +126,8 @@ LOCAL_SRC_FILES += \ core/java/android/view/IWindow.aidl \ core/java/android/view/IWindowManager.aidl \ core/java/android/view/IWindowSession.aidl \ + core/java/android/speech/IRecognitionListener.aidl \ + core/java/android/speech/IRecognitionService.aidl \ core/java/android/speech/tts/ITts.aidl \ core/java/android/speech/tts/ITtsCallback.aidl \ core/java/com/android/internal/app/IBatteryStats.aidl \ diff --git a/api/current.xml b/api/current.xml index e438f6e..2c9a087 100644 --- a/api/current.xml +++ b/api/current.xml @@ -26137,6 +26137,16 @@ <parameter name="flags" type="int"> </parameter> </method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="BluetoothClass.Device" extends="java.lang.Object" @@ -27197,6 +27207,16 @@ visibility="public" > </field> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ERROR" type="int" transient="false" @@ -27401,6 +27421,87 @@ > </method> </class> +<class name="ParcelUuid" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<constructor name="ParcelUuid" + type="android.bluetooth.ParcelUuid" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uuid" type="java.util.UUID"> +</parameter> +</constructor> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="fromString" + return="android.bluetooth.ParcelUuid" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uuid" type="java.lang.String"> +</parameter> +</method> +<method name="getUuid" + return="java.util.UUID" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="dest" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> </package> <package name="android.content" > @@ -35178,6 +35279,17 @@ visibility="public" > </field> +<field name="ACTION_DOCK_EVENT" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.DOCK_EVENT"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_EDIT" type="java.lang.String" transient="false" @@ -35893,6 +36005,17 @@ visibility="public" > </field> +<field name="CATEGORY_CAR_DOCK" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.category.CAR_DOCK"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CATEGORY_DEFAULT" type="java.lang.String" transient="false" @@ -35904,6 +36027,17 @@ visibility="public" > </field> +<field name="CATEGORY_DESK_DOCK" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.category.DESK_DOCK"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="CATEGORY_DEVELOPMENT_PREFERENCE" type="java.lang.String" transient="false" @@ -36123,6 +36257,50 @@ visibility="public" > </field> +<field name="EXTRA_DOCK_STATE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.extra.DOCK_STATE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DOCK_STATE_CAR" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DOCK_STATE_DESK" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_DOCK_STATE_UNDOCKED" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_DONT_KILL_APP" type="java.lang.String" transient="false" @@ -129286,6 +129464,17 @@ visibility="public" > </field> +<field name="TYPE_TEXT_FLAG_NO_SUGGESTIONS" + type="int" + transient="false" + volatile="false" + value="524288" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TYPE_TEXT_VARIATION_EMAIL_ADDRESS" type="int" transient="false" diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c index 83c7871..87fdc80 100644 --- a/cmds/keystore/netkeystore.c +++ b/cmds/keystore/netkeystore.c @@ -116,10 +116,13 @@ static int parse_strings(char *data, int data_len, int ntokens, ...) static int is_alnum_string(char *s) { + char *s0 = s; while (*s != 0) { - if (!isalnum(*s++)) return 0; + if (!isalnum(*s++)) { + LOGE("The string '%s' is not an alphanumeric string\n", s0); + return 0; + } } - LOGE("The string %s is not an alphanumeric string\n", s); return 1; } @@ -159,7 +162,9 @@ static void do_unlock(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) // no argument static void do_get_state(LPC_MARSHAL *cmd, LPC_MARSHAL *reply) { - reply->retcode = get_state(); + int s = get_state(); + if (DBG) LOGD("keystore state = %d\n", s); + reply->retcode = s; } // args of listkeys(): @@ -413,12 +418,10 @@ int server_main(const int argc, const char *argv[]) // read the command, execute and send the result back. if(read_marshal(s, &cmd)) goto err; - if (DBG) LOGD("new connection\n"); execute(&cmd, &reply); write_marshal(s, &reply); err: memset(&reply, 0, sizeof(LPC_MARSHAL)); - if (DBG) LOGD("closing connection\n"); close(s); } diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 73215d3..5397a69 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -61,6 +61,66 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) { decoder->start(); + if (gReproduceBug == 3) { + status_t err; + MediaBuffer *buffer; + MediaSource::ReadOptions options; + int64_t seekTimeUs = -1; + for (;;) { + err = decoder->read(&buffer, &options); + options.clearSeekTo(); + + bool shouldSeek = false; + if (err != OK) { + printf("reached EOF.\n"); + + shouldSeek = true; + } else { + int32_t units, scale; + CHECK(buffer->meta_data()->findInt32(kKeyTimeUnits, &units)); + CHECK(buffer->meta_data()->findInt32(kKeyTimeScale, &scale)); + int64_t timestamp = ((OMX_TICKS)units * 1000000) / scale; + + bool failed = false; + if (seekTimeUs >= 0) { + int64_t diff = timestamp - seekTimeUs; + + if (diff > 500000) { + printf("ERROR: "); + failed = true; + } + } + + printf("buffer has timestamp %lld us (%.2f secs)\n", + timestamp, timestamp / 1E6); + + buffer->release(); + buffer = NULL; + + if (failed) { + break; + } + + shouldSeek = ((double)rand() / RAND_MAX) < 0.1; + shouldSeek = false; + } + + seekTimeUs = -1; + + if (shouldSeek) { + seekTimeUs = (rand() * 30E6) / RAND_MAX; + options.setSeekTo(seekTimeUs); + + printf("seeking to %lld us (%.2f secs)\n", + seekTimeUs, seekTimeUs / 1E6); + } + } + + decoder->stop(); + + return; + } + int n = 0; int64_t startTime = getNowUs(); diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index c13893a..4217957 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -46,7 +46,6 @@ import android.util.Pair; import android.app.PendingIntent; import android.app.NotificationManager; import android.app.Notification; -import android.app.Activity; import android.Manifest; import java.io.FileDescriptor; @@ -471,6 +470,7 @@ public class AccountManagerService extends IAccountManager.Stub { } private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) { + cancelNotification(getSigninRequiredNotificationId(account)); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.beginTransaction(); try { @@ -523,7 +523,7 @@ public class AccountManagerService extends IAccountManager.Stub { checkAuthenticateAccountsPermission(account); long identityToken = clearCallingIdentity(); try { - cacheAuthToken(account, authTokenType, authToken); + saveAuthTokenToDatabase(account, authTokenType, authToken); } finally { restoreCallingIdentity(identityToken); } @@ -686,7 +686,8 @@ public class AccountManagerService extends IAccountManager.Stub { "the type and name should not be empty"); return; } - cacheAuthToken(new Account(name, type), authTokenType, authToken); + saveAuthTokenToDatabase(new Account(name, type), + authTokenType, authToken); } Intent intent = result.getParcelable(Constants.INTENT_KEY); @@ -714,11 +715,14 @@ public class AccountManagerService extends IAccountManager.Stub { Notification n = new Notification(android.R.drawable.stat_sys_warning, null, 0 /* when */); - final CharSequence subtitleFormatString = - mContext.getText(R.string.permission_request_notification_subtitle); + final String titleAndSubtitle = + mContext.getString(R.string.permission_request_notification_with_subtitle, + account.name); + final int index = titleAndSubtitle.indexOf('\n'); + final String title = titleAndSubtitle.substring(0, index); + final String subtitle = titleAndSubtitle.substring(index + 1); n.setLatestEventInfo(mContext, - mContext.getText(R.string.permission_request_notification_title), - String.format(subtitleFormatString.toString(), account.name), + title, subtitle, PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)); ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) .notify(getCredentialPermissionNotificationId(account, authTokenType, uid), n); @@ -1001,10 +1005,6 @@ public class AccountManagerService extends IAccountManager.Stub { } } - private boolean cacheAuthToken(Account account, String authTokenType, String authToken) { - return saveAuthTokenToDatabase(account, authTokenType, authToken); - } - private long getAccountId(SQLiteDatabase db, Account account) { Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 75a90c4..1209d0f 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -739,11 +739,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return false; } - // handle back key to go back to previous searchable, etc. - if (handleBackKey(keyCode, event)) { - return true; - } - if (keyCode == KeyEvent.KEYCODE_SEARCH) { // Consume search key for later use. return true; @@ -756,8 +751,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS launchQuerySearch(keyCode, actionKey.getQueryActionMsg()); return true; } - - return false; + + return super.onKeyDown(keyCode, event); } @Override @@ -767,11 +762,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return false; } - // handle back key to go back to previous searchable, etc. - if (handleBackKey(keyCode, event)) { - return true; - } - if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking() && !event.isCanceled()) { // If the search key is pressed, toggle between global and in-app search. If we are @@ -779,8 +769,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // just don't do anything. return toggleGlobalSearch(); } - - return false; + + return super.onKeyUp(keyCode, event); } /** @@ -1488,6 +1478,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** + * Checks if there are any previous searchable components in the history stack. + */ + private boolean hasPreviousComponent() { + return mPreviousComponents != null && !mPreviousComponents.isEmpty(); + } + + /** * Saves the previous component that was searched, so that we can go * back to it. */ @@ -1505,14 +1502,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * no previous component. */ private ComponentName popPreviousComponent() { - if (mPreviousComponents == null) { - return null; - } - int size = mPreviousComponents.size(); - if (size == 0) { + if (!hasPreviousComponent()) { return null; } - return mPreviousComponents.remove(size - 1); + return mPreviousComponents.remove(mPreviousComponents.size() - 1); } /** @@ -1520,25 +1513,22 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * * @return <code>true</code> if there was a previous component that we could go back to. */ - private boolean backToPreviousComponent(boolean doIt) { + private boolean backToPreviousComponent() { ComponentName previous = popPreviousComponent(); if (previous == null) { return false; } - - if (doIt) { - if (!show(previous, mAppSearchData, false)) { - Log.w(LOG_TAG, "Failed to switch to source " + previous); - return false; - } - - // must touch text to trigger suggestions - // TODO: should this be the text as it was when the user left - // the source that we are now going back to? - String query = mSearchAutoComplete.getText().toString(); - setUserQuery(query); + + if (!show(previous, mAppSearchData, false)) { + Log.w(LOG_TAG, "Failed to switch to source " + previous); + return false; } - + + // must touch text to trigger suggestions + // TODO: should this be the text as it was when the user left + // the source that we are now going back to? + String query = mSearchAutoComplete.getText().toString(); + setUserQuery(query); return true; } @@ -1763,74 +1753,49 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (DBG) Log.d(LOG_TAG, "onKeyPreIme(" + keyCode + "," + event + ")"); if (mSearchDialog.mSearchable == null) { return false; } if (keyCode == KeyEvent.KEYCODE_BACK) { if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { - // We release the back key, might we want to do - // something before the IME? - if (mSearchDialog.backToPreviousComponent(false)) { - getKeyDispatcherState().startTracking(event, this); - return true; - } - if (isInputMethodNotNeeded() || - (isEmpty() && getDropDownChildCount() >= getAdapterCount())) { + if (mSearchDialog.hasPreviousComponent() || isDismissingKeyboardPointless()) { getKeyDispatcherState().startTracking(event, this); return true; } - return false; // will dismiss soft keyboard if necessary } else if (event.getAction() == KeyEvent.ACTION_UP && event.isTracking() && !event.isCanceled()) { - if (mSearchDialog.backToPreviousComponent(true)) { + if (mSearchDialog.backToPreviousComponent()) { return true; - } - // If the drop-down obscures the keyboard, the user wouldn't see anything - // happening when pressing back, so we dismiss the entire dialog instead. - // - // also: if there is no text entered, we also want to dismiss the whole dialog, - // not just the soft keyboard. the exception to this is if there are shortcuts - // that aren't displayed (e.g are being obscured by the soft keyboard); in that - // case we want to dismiss the soft keyboard so the user can see the rest of the - // shortcuts. - if (isInputMethodNotNeeded() || - (isEmpty() && getDropDownChildCount() >= getAdapterCount())) { + } else if (isDismissingKeyboardPointless()) { mSearchDialog.cancel(); return true; } - return false; // will dismiss soft keyboard if necessary } } return false; } + // If the drop-down obscures the keyboard, or if the drop-down shows all suggestions, + // dismissing the keyboard is pointless, so we dismiss the entire dialog instead. + private boolean isDismissingKeyboardPointless() { + return (isInputMethodNotNeeded() || getDropDownChildCount() >= getAdapterCount()); + } + private int getAdapterCount() { final ListAdapter adapter = getAdapter(); return adapter == null ? 0 : adapter.getCount(); } } - - protected boolean handleBackKey(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && event.getRepeatCount() == 0) { - // Consume the event, to get an up at which point we execute. - event.startTracking(); - return true; - } - if (event.getAction() == KeyEvent.ACTION_UP && event.isTracking() - && !event.isCanceled()) { - if (backToPreviousComponent(true)) { - return true; - } - cancel(); - } - return true; + + @Override + public void onBackPressed() { + if (!backToPreviousComponent()) { + cancel(); } - return false; } - + /** * Implements OnItemClickListener */ diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java index 1fbbf78..6210380 100644 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -72,12 +72,10 @@ public final class BluetoothClass implements Parcelable { return Integer.toHexString(mClass); } - /** @hide */ public int describeContents() { return 0; } - /** @hide */ public static final Parcelable.Creator<BluetoothClass> CREATOR = new Parcelable.Creator<BluetoothClass>() { public BluetoothClass createFromParcel(Parcel in) { @@ -88,7 +86,6 @@ public final class BluetoothClass implements Parcelable { } }; - /** @hide */ public void writeToParcel(Parcel out, int flags) { out.writeInt(mClass); } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index b1861ac..f81ba73 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -349,12 +349,10 @@ public final class BluetoothDevice implements Parcelable { return mAddress; } - /** @hide */ public int describeContents() { return 0; } - /** @hide */ public static final Parcelable.Creator<BluetoothDevice> CREATOR = new Parcelable.Creator<BluetoothDevice>() { public BluetoothDevice createFromParcel(Parcel in) { @@ -365,7 +363,6 @@ public final class BluetoothDevice implements Parcelable { } }; - /** @hide */ public void writeToParcel(Parcel out, int flags) { out.writeString(mAddress); } @@ -503,7 +500,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ - public String[] getUuids() { + public ParcelUuid[] getUuids() { try { return sService.getRemoteUuids(mAddress); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -511,7 +508,7 @@ public final class BluetoothDevice implements Parcelable { } /** @hide */ - public int getServiceChannel(String uuid) { + public int getServiceChannel(ParcelUuid uuid) { try { return sService.getRemoteServiceChannel(mAddress, uuid); } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index c15bc20..409c744 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -16,10 +16,11 @@ package android.bluetooth; -import java.util.UUID; +import java.util.Arrays; +import java.util.HashSet; /** -* Static helper methods and constants to decode the UUID of remote devices. +* Static helper methods and constants to decode the ParcelUuid of remote devices. * @hide */ public final class BluetoothUuid { @@ -30,40 +31,99 @@ public final class BluetoothUuid { * The following 128 bit values are calculated as: * uuid * 2^96 + BASE_UUID */ - public static final UUID AudioSink = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB"); - public static final UUID AudioSource = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB"); - public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB"); - public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB"); - public static final UUID Handsfree = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB"); - public static final UUID AvrcpController = - UUID.fromString("0000110E-0000-1000-8000-00805F9B34FB"); - public static final UUID AvrcpTarget = UUID.fromString("0000110C-0000-1000-8000-00805F9B34FB"); - - public static boolean isAudioSource(UUID uuid) { + public static final ParcelUuid AudioSink = + ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AudioSource = + ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AdvAudioDist = + ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid HSP = + ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid Handsfree = + ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AvrcpController = + ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid AvrcpTarget = + ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB"); + public static final ParcelUuid ObexObjectPush = + ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb"); + + public static boolean isAudioSource(ParcelUuid uuid) { return uuid.equals(AudioSource); } - public static boolean isAudioSink(UUID uuid) { + public static boolean isAudioSink(ParcelUuid uuid) { return uuid.equals(AudioSink); } - public static boolean isAdvAudioDist(UUID uuid) { + public static boolean isAdvAudioDist(ParcelUuid uuid) { return uuid.equals(AdvAudioDist); } - public static boolean isHandsfree(UUID uuid) { + public static boolean isHandsfree(ParcelUuid uuid) { return uuid.equals(Handsfree); } - public static boolean isHeadset(UUID uuid) { + public static boolean isHeadset(ParcelUuid uuid) { return uuid.equals(HSP); } - public static boolean isAvrcpController(UUID uuid) { + public static boolean isAvrcpController(ParcelUuid uuid) { return uuid.equals(AvrcpController); } - public static boolean isAvrcpTarget(UUID uuid) { + public static boolean isAvrcpTarget(ParcelUuid uuid) { return uuid.equals(AvrcpTarget); } + + /** + * Returns true if ParcelUuid is present in uuidArray + * + * @param uuidArray - Array of ParcelUuids + * @param uuid + */ + public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) { + for (ParcelUuid element: uuidArray) { + if (element.equals(uuid)) return true; + } + return false; + } + + /** + * Returns true if there any common ParcelUuids in uuidA and uuidB. + * + * @param uuidA - List of ParcelUuids + * @param uuidB - List of ParcelUuids + * + */ + public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + if (uuidA == null && uuidB == null) return true; + if (uuidA == null || uuidB == null) return false; + + HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (Arrays.asList(uuidA)); + for (ParcelUuid uuid: uuidB) { + if (uuidSet.contains(uuid)) return true; + } + return false; + } + + /** + * Returns true if all the ParcelUuids in ParcelUuidB are present in + * ParcelUuidA + * + * @param uuidA - Array of ParcelUuidsA + * @param uuidB - Array of ParcelUuidsB + * + */ + public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) { + if (uuidA == null && uuidB == null) return true; + if (uuidA == null || uuidB == null) return false; + + HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (Arrays.asList(uuidA)); + for (ParcelUuid uuid: uuidB) { + if (!uuidSet.contains(uuid)) return false; + } + return true; + } + } diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index a11ceac..04c8ec9 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -16,6 +16,8 @@ package android.bluetooth; +import android.bluetooth.ParcelUuid; + /** * System private API for talking with the Bluetooth service. * @@ -50,8 +52,8 @@ interface IBluetooth String getRemoteName(in String address); int getRemoteClass(in String address); - String[] getRemoteUuids(in String address); - int getRemoteServiceChannel(in String address, String uuid); + ParcelUuid[] getRemoteUuids(in String address); + int getRemoteServiceChannel(in String address,in ParcelUuid uuid); boolean setPin(in String address, in byte[] pin); boolean setPasskey(in String address, int passkey); diff --git a/core/java/android/bluetooth/ParcelUuid.aidl b/core/java/android/bluetooth/ParcelUuid.aidl new file mode 100644 index 0000000..70bcc4b --- /dev/null +++ b/core/java/android/bluetooth/ParcelUuid.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 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 +** +** 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.bluetooth; + +parcelable ParcelUuid; diff --git a/core/java/android/bluetooth/ParcelUuid.java b/core/java/android/bluetooth/ParcelUuid.java new file mode 100644 index 0000000..27166a0 --- /dev/null +++ b/core/java/android/bluetooth/ParcelUuid.java @@ -0,0 +1,135 @@ +/* + * 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 + * + * 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.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.UUID; + +/** + * This class is a Parcelable wrapper around {@link UUID} which is an + * immutable representation of a 128-bit universally unique + * identifier. + */ +public final class ParcelUuid implements Parcelable { + + private final UUID mUuid; + + /** + * Constructor creates a ParcelUuid instance from the + * given {@link UUID}. + * + * @param uuid UUID + */ + public ParcelUuid(UUID uuid) { + mUuid = uuid; + } + + /** + * Creates a new ParcelUuid from a string representation of {@link UUID}. + * + * @param uuid + * the UUID string to parse. + * @return an ParcelUuid instance. + * @throws NullPointerException + * if {@code uuid} is {@code null}. + * @throws IllegalArgumentException + * if {@code uuid} is not formatted correctly. + */ + public static ParcelUuid fromString(String uuid) { + return new ParcelUuid(UUID.fromString(uuid)); + } + + /** + * Get the {@link UUID} represented by the ParcelUuid. + * + * @return UUID contained in the ParcelUuid. + */ + public UUID getUuid() { + return mUuid; + } + + /** + * Returns a string representation of the ParcelUuid + * For example: 0000110B-0000-1000-8000-00805F9B34FB will be the return value. + * + * @return a String instance. + */ + @Override + public String toString() { + return mUuid.toString(); + } + + + @Override + public int hashCode() { + return mUuid.hashCode(); + } + + /** + * Compares this ParcelUuid to another object for equality. If {@code object} + * is not {@code null}, is a ParcelUuid instance, and all bits are equal, then + * {@code true} is returned. + * + * @param object + * the {@code Object} to compare to. + * @return {@code true} if this ParcelUuid is equal to {@code object} + * or {@code false} if not. + */ + @Override + public boolean equals(Object object) { + if (object == null) { + return false; + } + + if (this == object) { + return true; + } + + if (!(object instanceof ParcelUuid)) { + return false; + } + + ParcelUuid that = (ParcelUuid) object; + + return (this.mUuid.equals(that.mUuid)); + } + + public static final Parcelable.Creator<ParcelUuid> CREATOR = + new Parcelable.Creator<ParcelUuid>() { + public ParcelUuid createFromParcel(Parcel source) { + long mostSigBits = source.readLong(); + long leastSigBits = source.readLong(); + UUID uuid = new UUID(mostSigBits, leastSigBits); + return new ParcelUuid(uuid); + } + + public ParcelUuid[] newArray(int size) { + return new ParcelUuid[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mUuid.getMostSignificantBits()); + dest.writeLong(mUuid.getLeastSignificantBits()); + } +} diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index 238792b..60b406d 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -221,26 +221,30 @@ public class ContentProviderOperation implements Parcelable { } else if (mType == TYPE_UPDATE) { numRows = provider.update(mUri, values, mSelection, selectionArgs); } else if (mType == TYPE_ASSERT) { - // Build projection map from expected values - final ArrayList<String> projectionList = new ArrayList<String>(); - for (Map.Entry<String, Object> entry : values.valueSet()) { - projectionList.add(entry.getKey()); - } - // Assert that all rows match expected values - final String[] projection = projectionList.toArray(new String[projectionList.size()]); + String[] projection = null; + if (values != null) { + // Build projection map from expected values + final ArrayList<String> projectionList = new ArrayList<String>(); + for (Map.Entry<String, Object> entry : values.valueSet()) { + projectionList.add(entry.getKey()); + } + projection = projectionList.toArray(new String[projectionList.size()]); + } final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null); - numRows = cursor.getCount(); try { - while (cursor.moveToNext()) { - for (int i = 0; i < projection.length; i++) { - final String cursorValue = cursor.getString(i); - final String expectedValue = values.getAsString(projection[i]); - if (!TextUtils.equals(cursorValue, expectedValue)) { - // Throw exception when expected values don't match - throw new OperationApplicationException("Found value " + cursorValue - + " when expected " + expectedValue + " for column " - + projection[i]); + numRows = cursor.getCount(); + if (projection != null) { + while (cursor.moveToNext()) { + for (int i = 0; i < projection.length; i++) { + final String cursorValue = cursor.getString(i); + final String expectedValue = values.getAsString(projection[i]); + if (!TextUtils.equals(cursorValue, expectedValue)) { + // Throw exception when expected values don't match + throw new OperationApplicationException("Found value " + cursorValue + + " when expected " + expectedValue + " for column " + + projection[i]); + } } } } @@ -395,12 +399,19 @@ public class ContentProviderOperation implements Parcelable { /** Create a ContentProviderOperation from this {@link Builder}. */ public ContentProviderOperation build() { - if (mType == TYPE_UPDATE || mType == TYPE_ASSERT) { + if (mType == TYPE_UPDATE) { if ((mValues == null || mValues.size() == 0) && (mValuesBackReferences == null || mValuesBackReferences.size() == 0)) { throw new IllegalArgumentException("Empty values"); } } + if (mType == TYPE_ASSERT) { + if ((mValues == null || mValues.size() == 0) + && (mValuesBackReferences == null || mValuesBackReferences.size() == 0) + && (mExpectedCount == null)) { + throw new IllegalArgumentException("Empty values"); + } + } return new ContentProviderOperation(this); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index c053ace..fc977c8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -529,6 +529,8 @@ import java.util.Set; * <li> {@link #CATEGORY_HOME} * <li> {@link #CATEGORY_PREFERENCE} * <li> {@link #CATEGORY_TEST} + * <li> {@link #CATEGORY_CAR_DOCK} + * <li> {@link #CATEGORY_DESK_DOCK} * </ul> * * <h3>Standard Extra Data</h3> @@ -1861,12 +1863,29 @@ public class Intent implements Parcelable { */ public static final String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST = "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"; + /** + * An activity to run when device is inserted into a car dock. + * Used with {@link #ACTION_MAIN} to launch an activity. + * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK"; + /** + * An activity to run when device is inserted into a car dock. + * Used with {@link #ACTION_MAIN} to launch an activity. + * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_DESK_DOCK = "android.intent.category.DESK_DOCK"; /** * Broadcast Action: The phone was docked or undocked. Includes the extra * field {@link #EXTRA_DOCK_STATE}, containing the current dock state. - * @hide + * This is intended for monitoring the current dock state. + * To launch an activity from a dock state change, use {@link #CATEGORY_CAR_DOCK} + * or {@link #CATEGORY_DESK_DOCK} instead. */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DOCK_EVENT = "android.intent.action.DOCK_EVENT"; // --------------------------------------------------------------------- @@ -2005,28 +2024,24 @@ public class Intent implements Parcelable { * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED}, * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}. - * @hide */ public static final String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE"; /** * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE} * to represent that the phone is not in any dock. - * @hide */ public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; /** * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE} * to represent that the phone is in a desk dock. - * @hide */ public static final int EXTRA_DOCK_STATE_DESK = 1; /** * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE} * to represent that the phone is in a car dock. - * @hide */ public static final int EXTRA_DOCK_STATE_CAR = 2; diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index d57155c..1de971b 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -506,6 +506,13 @@ public final class Calendar { * <P>Type: INTEGER (boolean, readonly)</P> */ public static final String CAN_INVITE_OTHERS = "canInviteOthers"; + + /** + * The owner account for this calendar, based on the calendar (foreign + * key into the calendars table). + * <P>Type: String</P> + */ + public static final String OWNER_ACCOUNT = "ownerAccount"; } /** diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 667ec5a..d87018d 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -533,7 +533,7 @@ public class Contacts { Uri photoUri = Uri.withAppendedPath(person, Contacts.Photos.CONTENT_DIRECTORY); Cursor cursor = cr.query(photoUri, new String[]{Photos.DATA}, null, null, null); try { - if (!cursor.moveToNext()) { + if (cursor == null || !cursor.moveToNext()) { return null; } byte[] data = cursor.getBlob(0); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index afa0a7c..6eaf9dd 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -1039,6 +1039,7 @@ public final class ContactsContract { public static final int TYPE_WORK_MOBILE = 17; public static final int TYPE_WORK_PAGER = 18; public static final int TYPE_ASSISTANT = 19; + public static final int TYPE_MMS = 20; /** * The phone number as the user entered it. diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 9c687e2..be8c777 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -27,6 +27,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothA2dp; +import android.bluetooth.ParcelUuid; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -42,7 +43,6 @@ import java.io.PrintWriter; import java.util.HashMap; import java.util.HashSet; import java.util.Set; -import java.util.UUID; public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String TAG = "BluetoothA2dpService"; @@ -188,15 +188,9 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } private boolean isSinkDevice(BluetoothDevice device) { - String uuids[] = mBluetoothService.getRemoteUuids(device.getAddress()); - UUID uuid; - if (uuids != null) { - for (String deviceUuid: uuids) { - uuid = UUID.fromString(deviceUuid); - if (BluetoothUuid.isAudioSink(uuid)) { - return true; - } - } + ParcelUuid[] uuids = mBluetoothService.getRemoteUuids(device.getAddress()); + if (uuids != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) { + return true; } return false; } @@ -229,18 +223,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { for (String path: paths) { String address = mBluetoothService.getAddressFromObjectPath(path); BluetoothDevice device = mAdapter.getRemoteDevice(address); - String []uuids = mBluetoothService.getRemoteUuids(address); - if (uuids != null) - for (String uuid: uuids) { - UUID remoteUuid = UUID.fromString(uuid); - if (BluetoothUuid.isAudioSink(remoteUuid) || - BluetoothUuid.isAudioSource(remoteUuid) || - BluetoothUuid.isAdvAudioDist(remoteUuid)) { - addAudioSink(device); - break; - } + ParcelUuid[] remoteUuids = mBluetoothService.getRemoteUuids(address); + if (remoteUuids != null) + if (BluetoothUuid.containsAnyUuid(remoteUuids, + new ParcelUuid[] {BluetoothUuid.AudioSink, + BluetoothUuid.AdvAudioDist})) { + addAudioSink(device); } - } + } } mAudioManager.setParameters(BLUETOOTH_ENABLED+"=true"); } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 1ed5c49..ba53307 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; +import android.bluetooth.ParcelUuid; import android.content.Context; import android.content.Intent; import android.os.Handler; @@ -28,7 +29,6 @@ import android.os.Message; import android.util.Log; import java.util.HashMap; -import java.util.UUID; /** * TODO: Move this to @@ -501,7 +501,7 @@ class BluetoothEventLoop { } boolean authorized = false; - UUID uuid = UUID.fromString(deviceUuid); + ParcelUuid uuid = ParcelUuid.fromString(deviceUuid); // Bluez sends the UUID of the local service being accessed, _not_ the // remote service if (mBluetoothService.isEnabled() && diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index de0cad7..c0e4f34 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -24,11 +24,12 @@ package android.server; -import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.IBluetooth; +import android.bluetooth.ParcelUuid; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -51,7 +52,6 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; public class BluetoothService extends IBluetooth.Stub { @@ -967,22 +967,26 @@ public class BluetoothService extends IBluetooth.Stub { /** - * Gets the remote features encoded as bit mask. - * - * Note: This method may be obsoleted soon. + * Gets the UUIDs supported by the remote device * - * @return String array of 128bit UUIDs + * @return array of 128bit ParcelUuids */ - public synchronized String[] getRemoteUuids(String address) { + public synchronized ParcelUuid[] getRemoteUuids(String address) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothAdapter.checkBluetoothAddress(address)) { return null; } String value = getRemoteDeviceProperty(address, "UUIDs"); - String[] uuids = null; + if (value == null) return null; + + String[] uuidStrings = null; // The UUIDs are stored as a "," separated string. - if (value != null) - uuids = value.split(","); + uuidStrings = value.split(","); + ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length]; + + for (int i = 0; i < uuidStrings.length; i++) { + uuids[i] = ParcelUuid.fromString(uuidStrings[i]); + } return uuids; } @@ -990,16 +994,17 @@ public class BluetoothService extends IBluetooth.Stub { * Gets the rfcomm channel associated with the UUID. * * @param address Address of the remote device - * @param uuid UUID of the service attribute + * @param uuid ParcelUuid of the service attribute * * @return rfcomm channel associated with the service attribute */ - public int getRemoteServiceChannel(String address, String uuid) { + public int getRemoteServiceChannel(String address, ParcelUuid uuid) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!BluetoothAdapter.checkBluetoothAddress(address)) { return BluetoothDevice.ERROR; } - return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004); + return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(), + 0x0004); } public synchronized boolean setPin(String address, byte[] pin) { @@ -1148,11 +1153,11 @@ public class BluetoothService extends IBluetooth.Stub { mBondState.getAttempt(address), getRemoteName(address)); if (bondState == BluetoothDevice.BOND_BONDED) { - String[] uuids = getRemoteUuids(address); + ParcelUuid[] uuids = getRemoteUuids(address); if (uuids == null) { pw.printf("\tuuids = null\n"); } else { - for (String uuid : uuids) { + for (ParcelUuid uuid : uuids) { pw.printf("\t" + uuid + "\n"); } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index cd5cf10..da8d62c0 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -321,7 +321,7 @@ public abstract class WallpaperService extends Service { * Called as the user performs touch-screen interaction with the * window that is currently showing this wallpaper. Note that the * events you receive here are driven by the actual application the - * user is interacting with, so if it is slow you will get viewer + * user is interacting with, so if it is slow you will get fewer * move events. */ public void onTouchEvent(MotionEvent event) { diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl new file mode 100644 index 0000000..2da2258 --- /dev/null +++ b/core/java/android/speech/IRecognitionListener.aidl @@ -0,0 +1,60 @@ +/* + * 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 + * + * 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.speech; + +import android.os.Bundle; +import android.speech.RecognitionResult; + +/** + * Listener for speech recognition events, used with RecognitionService. + * This gives you both the final recognition results, as well as various + * intermediate events that can be used to show visual feedback to the user. + * {@hide} + */ +interface IRecognitionListener { + /** Called when the endpointer is ready for the user to start speaking. */ + void onReadyForSpeech(in Bundle noiseParams); + + /** The user has started to speak. */ + void onBeginningOfSpeech(); + + /** The sound level in the audio stream has changed. */ + void onRmsChanged(in float rmsdB); + + /** + * More sound has been received. Buffer is a byte buffer containing + * a sequence of 16-bit shorts. + */ + void onBufferReceived(in byte[] buffer); + + /** Called after the user stops speaking. */ + void onEndOfSpeech(); + + /** + * A network or recognition error occurred. The code is defined in + * {@link android.speech.RecognitionResult} + */ + void onError(in int error); + + /** + * Called when recognition results are ready. + * @param results: an ordered list of the most likely results (N-best list). + * @param key: a key associated with the results. The same results can + * be retrieved asynchronously later using the key, if available. + */ + void onResults(in List<RecognitionResult> results, long key); +} diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl new file mode 100644 index 0000000..a18c380 --- /dev/null +++ b/core/java/android/speech/IRecognitionService.aidl @@ -0,0 +1,37 @@ +/* + * 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 + * + * 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.speech; + +import android.content.Intent; +import android.speech.IRecognitionListener; +import android.speech.RecognitionResult; + +// A Service interface to speech recognition. Call startListening when +// you want to begin capturing audio; RecognitionService will automatically +// determine when the user has finished speaking, stream the audio to the +// recognition servers, and notify you when results are ready. +/** {@hide} */ +interface IRecognitionService { + // Start listening for speech. Can only call this from one thread at once. + // see RecognizerIntent.java for constants used to specify the intent. + void startListening(in Intent recognizerIntent, + in IRecognitionListener listener); + + List<RecognitionResult> getRecognitionResults(in long key); + + void cancel(); +} diff --git a/core/java/android/speech/RecognitionResult.aidl b/core/java/android/speech/RecognitionResult.aidl new file mode 100644 index 0000000..59e53ab --- /dev/null +++ b/core/java/android/speech/RecognitionResult.aidl @@ -0,0 +1,19 @@ +/* + * 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 + * + * 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.speech; + +parcelable RecognitionResult; diff --git a/core/java/android/speech/RecognitionResult.java b/core/java/android/speech/RecognitionResult.java new file mode 100644 index 0000000..95715ee --- /dev/null +++ b/core/java/android/speech/RecognitionResult.java @@ -0,0 +1,220 @@ +/* + * 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 + * + * 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.speech; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * RecognitionResult is a passive object that stores a single recognized query + * and its search result. + * + * TODO: Revisit and improve this class, reconciling the different types of actions and + * the different ways they are represented. Maybe we should have a separate result object + * for each type, and put them (type/value) in bundle? + * {@hide} + */ +public class RecognitionResult implements Parcelable { + /** + * Status of the recognize request. + */ + public static final int NETWORK_TIMEOUT = 1; // Network operation timed out. + + public static final int NETWORK_ERROR = 2; // Other network related errors. + + public static final int AUDIO_ERROR = 3; // Audio recording error. + + public static final int SERVER_ERROR = 4; // Server sends error status. + + public static final int CLIENT_ERROR = 5; // Other client side errors. + + public static final int SPEECH_TIMEOUT = 6; // No speech input + + public static final int NO_MATCH = 7; // No recognition result matched. + + public static final int SERVICE_BUSY = 8; // RecognitionService busy. + + /** + * Type of the recognition results. + */ + public static final int RAW_RECOGNITION_RESULT = 0; + + public static final int WEB_SEARCH_RESULT = 1; + + public static final int CONTACT_RESULT = 2; + + public static final int ACTION_RESULT = 3; + + /** + * A factory method to create a raw RecognitionResult + * + * @param sentence the recognized text. + */ + public static RecognitionResult newRawRecognitionResult(String sentence) { + return new RecognitionResult(RAW_RECOGNITION_RESULT, sentence, null, null); + } + + /** + * A factory method to create a RecognitionResult for contacts. + * + * @param contact the contact name. + * @param phoneType the phone type. + * @param callAction whether this result included a command to "call", or + * just the contact name. + */ + public static RecognitionResult newContactResult(String contact, int phoneType, + boolean callAction) { + return new RecognitionResult(CONTACT_RESULT, contact, phoneType, callAction); + } + + /** + * A factory method to create a RecognitionResult for a web search query. + * + * @param query the query string. + * @param html the html page of the search result. + * @param url the url that performs the search with the query. + */ + public static RecognitionResult newWebResult(String query, String html, String url) { + return new RecognitionResult(WEB_SEARCH_RESULT, query, html, url); + } + + /** + * A factory method to create a RecognitionResult for an action. + * + * @param action the action type + * @param query the query string associated with that action. + */ + public static RecognitionResult newActionResult(int action, String query) { + return new RecognitionResult(ACTION_RESULT, action, query); + } + + public static final Parcelable.Creator<RecognitionResult> CREATOR = + new Parcelable.Creator<RecognitionResult>() { + + public RecognitionResult createFromParcel(Parcel in) { + return new RecognitionResult(in); + } + + public RecognitionResult[] newArray(int size) { + return new RecognitionResult[size]; + } + }; + + /** + * Result type. + */ + public final int mResultType; + + /** + * The recognized string when mResultType is WEB_SEARCH_RESULT. The name of + * the contact when mResultType is CONTACT_RESULT. The relevant query when + * mResultType is ACTION_RESULT. + */ + public final String mText; + + /** + * The HTML result page for the query. If this is null, then the application + * must use the url field to get the HTML result page. + */ + public final String mHtml; + + /** + * The url to get the result page for the query string. The application must + * use this url instead of performing the search with the query. + */ + public final String mUrl; + + /** + * Phone number type. This is valid only when mResultType == CONTACT_RESULT. + */ + public final int mPhoneType; + + /** + * Action type. This is valid only when mResultType == ACTION_RESULT. + */ + public final int mAction; + + /** + * Whether a contact recognition result included a command to "call". This + * is valid only when mResultType == CONTACT_RESULT. + */ + public final boolean mCallAction; + + private RecognitionResult(int type, int action, String query) { + mResultType = type; + mAction = action; + mText = query; + mHtml = null; + mUrl = null; + mPhoneType = -1; + mCallAction = false; + } + + private RecognitionResult(int type, String query, String html, String url) { + mResultType = type; + mText = query; + mHtml = html; + mUrl = url; + mPhoneType = -1; + mAction = -1; + mCallAction = false; + } + + private RecognitionResult(int type, String query, int phoneType, boolean callAction) { + mResultType = type; + mText = query; + mPhoneType = phoneType; + mHtml = null; + mUrl = null; + mAction = -1; + mCallAction = callAction; + } + + private RecognitionResult(Parcel in) { + mResultType = in.readInt(); + mText = in.readString(); + mHtml = in.readString(); + mUrl = in.readString(); + mPhoneType = in.readInt(); + mAction = in.readInt(); + mCallAction = (in.readInt() == 1); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mResultType); + out.writeString(mText); + out.writeString(mHtml); + out.writeString(mUrl); + out.writeInt(mPhoneType); + out.writeInt(mAction); + out.writeInt(mCallAction ? 1 : 0); + } + + @Override + public String toString() { + String resultType[] = { + "RAW", "WEB", "CONTACT", "ACTION" + }; + return "[type=" + resultType[mResultType] + ", text=" + mText + ", mUrl=" + mUrl + + ", html=" + mHtml + ", mAction=" + mAction + ", mCallAction=" + mCallAction + "]"; + } + + public int describeContents() { + // no special description + return 0; + } +} diff --git a/core/java/android/speech/RecognitionServiceUtil.java b/core/java/android/speech/RecognitionServiceUtil.java new file mode 100644 index 0000000..4207543 --- /dev/null +++ b/core/java/android/speech/RecognitionServiceUtil.java @@ -0,0 +1,101 @@ +/* + * 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 + * + * 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.speech; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.speech.RecognitionResult; +import android.util.Log; + +import java.util.List; + +/** + * Utils for Google's network-based speech recognizer, which lets you perform + * speech-to-text translation through RecognitionService. IRecognitionService + * and IRecognitionListener are the core interfaces; you begin recognition + * through IRecognitionService and subscribe to callbacks about when the user + * stopped speaking, results come in, errors, etc. through IRecognitionListener. + * RecognitionServiceUtil includes default IRecognitionListener and + * ServiceConnection implementations to reduce the amount of boilerplate. + * + * The Service provides no user interface. See RecognitionActivity if you + * want the standard voice search UI. + * + * Below is a small skeleton of how to use the recognizer: + * + * ServiceConnection conn = new RecognitionServiceUtil.Connection(); + * mContext.bindService(RecognitionServiceUtil.sDefaultIntent, + * conn, Context.BIND_AUTO_CREATE); + * IRecognitionListener listener = new RecognitionServiceWrapper.NullListener() { + * public void onResults(List<String> results) { + * // Do something with recognition transcripts + * } + * } + * + * // Must wait for conn.mService to be populated, then call below + * conn.mService.startListening(null, listener); + * + * {@hide} + */ +public class RecognitionServiceUtil { + public static final Intent sDefaultIntent = new Intent( + RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + + // Recognize request parameters + public static final String USE_LOCATION = "useLocation"; + public static final String CONTACT_AUTH_TOKEN = "contactAuthToken"; + + // Bundles + public static final String NOISE_LEVEL = "NoiseLevel"; + public static final String SIGNAL_NOISE_RATIO = "SignalNoiseRatio"; + + private RecognitionServiceUtil() {} + + /** + * IRecognitionListener which does nothing in response to recognition + * callbacks. You can subclass from this and override only the methods + * whose events you want to respond to. + */ + public static class NullListener extends IRecognitionListener.Stub { + public void onReadyForSpeech(Bundle bundle) {} + public void onBeginningOfSpeech() {} + public void onRmsChanged(float rmsdB) {} + public void onBufferReceived(byte[] buf) {} + public void onEndOfSpeech() {} + public void onError(int error) {} + public void onResults(List<RecognitionResult> results, long key) {} + } + + /** + * Basic ServiceConnection which just records mService variable. + */ + public static class Connection implements ServiceConnection { + public IRecognitionService mService; + + public synchronized void onServiceConnected(ComponentName name, IBinder service) { + mService = IRecognitionService.Stub.asInterface(service); + } + + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + } +} diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index d50684a..14b8308 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -128,6 +128,15 @@ public interface InputType { */ public static final int TYPE_TEXT_FLAG_IME_MULTI_LINE = 0x00040000; + /** + * Flag for {@link #TYPE_CLASS_TEXT}: the input method does not need to + * display any dictionary-based candidates. This is useful for text views that + * do not contain words from the language and do not benefit from any + * dictionary-based completions or corrections. It overrides the + * {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} value when set. + */ + public static final int TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000; + // ---------------------------------------------------------------------- /** diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 38881d3..f736f85 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -431,6 +431,7 @@ public class QwertyKeyListener extends BaseKeyListener { PICKER_SETS.put('z', "\u017A\u017C\u017E"); PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT, "\u2026\u00A5\u2022\u00AE\u00A9\u00B1[]{}\\"); + PICKER_SETS.put('/', "\\"); // From packages/inputmethods/LatinIME/res/xml/kbd_symbols.xml diff --git a/core/java/android/text/util/Rfc822InputFilter.java b/core/java/android/text/util/Rfc822InputFilter.java new file mode 100644 index 0000000..8c8b7fc --- /dev/null +++ b/core/java/android/text/util/Rfc822InputFilter.java @@ -0,0 +1,58 @@ +package android.text.util; + +import android.text.InputFilter; +import android.text.Spanned; +import android.text.SpannableStringBuilder; + +/** + * Implements special address cleanup rules: + * The first space key entry following an "@" symbol that is followed by any combination + * of letters and symbols, including one+ dots and zero commas, should insert an extra + * comma (followed by the space). + * + * @hide + */ +public class Rfc822InputFilter implements InputFilter { + + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, + int dstart, int dend) { + + // quick check - did they enter a single space? + if (end-start != 1 || source.charAt(start) != ' ') { + return null; + } + + // determine if the characters before the new space fit the pattern + // follow backwards and see if we find a comma, dot, or @ + int scanBack = dstart; + boolean dotFound = false; + while (scanBack > 0) { + char c = dest.charAt(--scanBack); + switch (c) { + case '.': + dotFound = true; // one or more dots are req'd + break; + case ',': + return null; + case '@': + if (!dotFound) { + return null; + } + // we have found a comma-insert case. now just do it + // in the least expensive way we can. + if (source instanceof Spanned) { + SpannableStringBuilder sb = new SpannableStringBuilder(","); + sb.append(source); + return sb; + } else { + return ", "; + } + default: + // just keep going + } + } + + // no termination cases were found, so don't edit the input + return null; + } +} diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 94acd3f..e5985c1 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -458,11 +458,12 @@ public abstract class LayoutInflater { public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor constructor = sConstructorMap.get(name); + Class clazz = null; try { if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it - Class clazz = mContext.getClassLoader().loadClass( + clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name); if (mFilter != null && clazz != null) { @@ -480,7 +481,7 @@ public abstract class LayoutInflater { Boolean allowedState = mFilterMap.get(name); if (allowedState == null) { // New class -- remember whether it is allowed - Class clazz = mContext.getClassLoader().loadClass( + clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name); boolean allowed = clazz != null && mFilter.onLoadClass(clazz); @@ -511,7 +512,7 @@ public abstract class LayoutInflater { } catch (Exception e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " - + (constructor == null ? "<unknown>" : constructor.getClass().getName())); + + (clazz == null ? "<unknown>" : clazz.getName())); ie.initCause(e); throw ie; } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index c40107b..bd38b2d 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -361,8 +361,6 @@ public interface WindowManagerPolicy { public final int OFF_BECAUSE_OF_USER = 1; /** Screen turned off because of timeout */ public final int OFF_BECAUSE_OF_TIMEOUT = 2; - /** Screen turned off because of proximity sensor */ - public final int OFF_BECAUSE_OF_PROXIMITY_SENSOR = 3; /** * Magic constant to {@link IWindowManager#setRotation} to not actually diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java index 5a164f8..c10355c 100644 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -17,8 +17,18 @@ package android.webkit; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.media.MediaPlayer; import android.media.MediaPlayer.OnPreparedListener; +import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; +import android.net.http.EventHandler; +import android.net.http.Headers; +import android.net.http.RequestHandle; +import android.net.http.RequestQueue; +import android.net.http.SslCertificate; +import android.net.http.SslError; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -30,182 +40,389 @@ import android.view.View; import android.view.ViewGroup; import android.webkit.ViewManager.ChildView; import android.widget.AbsoluteLayout; +import android.widget.ImageView; import android.widget.MediaController; import android.widget.VideoView; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.HashMap; +import java.util.Map; /** * <p>Proxy for HTML5 video views. */ -class HTML5VideoViewProxy extends Handler { +class HTML5VideoViewProxy extends Handler + implements MediaPlayer.OnPreparedListener, + MediaPlayer.OnCompletionListener { // Logging tag. private static final String LOGTAG = "HTML5VideoViewProxy"; // Message Ids for WebCore thread -> UI thread communication. private static final int INIT = 100; private static final int PLAY = 101; + private static final int SET_POSTER = 102; + private static final int SEEK = 103; + private static final int PAUSE = 104; + // Message Ids to be handled on the WebCore thread + private static final int PREPARED = 200; + private static final int ENDED = 201; + + // The C++ MediaPlayerPrivateAndroid object. + int mNativePointer; + // The handler for WebCore thread messages; + private Handler mWebCoreHandler; // The WebView instance that created this view. private WebView mWebView; // The ChildView instance used by the ViewManager. private ChildView mChildView; - // The VideoView instance. Note that we could - // also access this via mChildView.mView but it would - // always require cast, so it is more convenient to store - // it here as well. - private HTML5VideoView mVideoView; - - // A VideoView subclass that responds to double-tap - // events by going fullscreen. - class HTML5VideoView extends VideoView { - // Used to save the layout parameters if the view - // is changed to fullscreen. - private AbsoluteLayout.LayoutParams mEmbeddedLayoutParams; - // Flag that denotes whether the view is fullscreen or not. - private boolean mIsFullscreen; - // Used to save the current playback position when - // transitioning to/from fullscreen. - private int mPlaybackPosition; - // The callback object passed to the host application. This callback - // is invoked when the host application dismisses our VideoView - // (e.g. the user presses the back key). - private WebChromeClient.CustomViewCallback mCallback = - new WebChromeClient.CustomViewCallback() { - public void onCustomViewHidden() { - playEmbedded(); + // The poster image to be shown when the video is not playing. + private ImageView mPosterView; + // The poster downloader. + private PosterDownloader mPosterDownloader; + // The seek position. + private int mSeekPosition; + // A helper class to control the playback. This executes on the UI thread! + private static final class VideoPlayer { + // The proxy that is currently playing (if any). + private static HTML5VideoViewProxy mCurrentProxy; + // The VideoView instance. This is a singleton for now, at least until + // http://b/issue?id=1973663 is fixed. + private static VideoView mVideoView; + + private static final WebChromeClient.CustomViewCallback mCallback = + new WebChromeClient.CustomViewCallback() { + public void onCustomViewHidden() { + // At this point the videoview is pretty much destroyed. + // It listens to SurfaceHolder.Callback.SurfaceDestroyed event + // which happens when the video view is detached from its parent + // view. This happens in the WebChromeClient before this method + // is invoked. + mCurrentProxy.playbackEnded(); + mCurrentProxy = null; + mVideoView = null; + } + }; + + public static void play(String url, int time, HTML5VideoViewProxy proxy, + WebChromeClient client) { + if (mCurrentProxy != null) { + // Some other video is already playing. Notify the caller that its playback ended. + proxy.playbackEnded(); + return; } - }; + mCurrentProxy = proxy; + mVideoView = new VideoView(proxy.getContext()); + mVideoView.setWillNotDraw(false); + mVideoView.setMediaController(new MediaController(proxy.getContext())); + mVideoView.setVideoURI(Uri.parse(url)); + mVideoView.setOnCompletionListener(proxy); + mVideoView.setOnPreparedListener(proxy); + mVideoView.seekTo(time); + mVideoView.start(); + client.onShowCustomView(mVideoView, mCallback); + } - // The OnPreparedListener, used to automatically resume - // playback when transitioning to/from fullscreen. - private MediaPlayer.OnPreparedListener mPreparedListener = - new MediaPlayer.OnPreparedListener() { - public void onPrepared(MediaPlayer mp) { - resumePlayback(); + public static void seek(int time, HTML5VideoViewProxy proxy) { + if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) { + mVideoView.seekTo(time); } - }; + } - HTML5VideoView(Context context) { - super(context); + public static void pause(HTML5VideoViewProxy proxy) { + if (mCurrentProxy == proxy && mVideoView != null) { + mVideoView.pause(); + } } + } + + // A bunch event listeners for our VideoView + // MediaPlayer.OnPreparedListener + public void onPrepared(MediaPlayer mp) { + Message msg = Message.obtain(mWebCoreHandler, PREPARED); + Map<String, Object> map = new HashMap<String, Object>(); + map.put("dur", new Integer(mp.getDuration())); + map.put("width", new Integer(mp.getVideoWidth())); + map.put("height", new Integer(mp.getVideoHeight())); + msg.obj = map; + mWebCoreHandler.sendMessage(msg); + } - void savePlaybackPosition() { - if (isPlaying()) { - mPlaybackPosition = getCurrentPosition(); + // MediaPlayer.OnCompletionListener; + public void onCompletion(MediaPlayer mp) { + playbackEnded(); + } + + public void playbackEnded() { + Message msg = Message.obtain(mWebCoreHandler, ENDED); + mWebCoreHandler.sendMessage(msg); + } + + // Handler for the messages from WebCore thread to the UI thread. + @Override + public void handleMessage(Message msg) { + // This executes on the UI thread. + switch (msg.what) { + case INIT: { + mPosterView = new ImageView(mWebView.getContext()); + mChildView.mView = mPosterView; + break; + } + case PLAY: { + String url = (String) msg.obj; + WebChromeClient client = mWebView.getWebChromeClient(); + if (client != null) { + VideoPlayer.play(url, mSeekPosition, this, client); + } + break; + } + case SET_POSTER: { + Bitmap poster = (Bitmap) msg.obj; + mPosterView.setImageBitmap(poster); + break; + } + case SEEK: { + Integer time = (Integer) msg.obj; + mSeekPosition = time; + VideoPlayer.seek(mSeekPosition, this); + break; + } + case PAUSE: { + VideoPlayer.pause(this); + break; } } + } + + // Everything below this comment executes on the WebCore thread, except for + // the EventHandler methods, which are called on the network thread. + + // A helper class that knows how to download posters + private static final class PosterDownloader implements EventHandler { + // The request queue. This is static as we have one queue for all posters. + private static RequestQueue mRequestQueue; + private static int mQueueRefCount = 0; + // The poster URL + private String mUrl; + // The proxy we're doing this for. + private final HTML5VideoViewProxy mProxy; + // The poster bytes. We only touch this on the network thread. + private ByteArrayOutputStream mPosterBytes; + // The request handle. We only touch this on the WebCore thread. + private RequestHandle mRequestHandle; + // The response status code. + private int mStatusCode; + // The response headers. + private Headers mHeaders; + // The handler to handle messages on the WebCore thread. + private Handler mHandler; - void resumePlayback() { - seekTo(mPlaybackPosition); - start(); - setOnPreparedListener(null); + public PosterDownloader(String url, HTML5VideoViewProxy proxy) { + mUrl = url; + mProxy = proxy; + mHandler = new Handler(); + } + // Start the download. Called on WebCore thread. + public void start() { + retainQueue(); + mRequestHandle = mRequestQueue.queueRequest(mUrl, "GET", null, this, null, 0); + } + // Cancel the download if active and release the queue. Called on WebCore thread. + public void cancelAndReleaseQueue() { + if (mRequestHandle != null) { + mRequestHandle.cancel(); + mRequestHandle = null; + } + releaseQueue(); + } + // EventHandler methods. Executed on the network thread. + public void status(int major_version, + int minor_version, + int code, + String reason_phrase) { + mStatusCode = code; } - void playEmbedded() { - // Attach to the WebView. - mChildView.attachViewOnUIThread(mEmbeddedLayoutParams); - // Make sure we're visible - setVisibility(View.VISIBLE); - // Set the onPrepared listener so we start - // playing when the video view is reattached - // and its surface is recreated. - setOnPreparedListener(mPreparedListener); - mIsFullscreen = false; + public void headers(Headers headers) { + mHeaders = headers; } - void playFullScreen() { - WebChromeClient client = mWebView.getWebChromeClient(); - if (client == null) { - return; + public void data(byte[] data, int len) { + if (mPosterBytes == null) { + mPosterBytes = new ByteArrayOutputStream(); } - // Save the current layout params. - mEmbeddedLayoutParams = - (AbsoluteLayout.LayoutParams) getLayoutParams(); - // Detach from the WebView. - mChildView.removeViewOnUIThread(); - // Attach to the browser UI. - client.onShowCustomView(this, mCallback); - // Set the onPrepared listener so we start - // playing when after the video view is reattached - // and its surface is recreated. - setOnPreparedListener(mPreparedListener); - mIsFullscreen = true; + mPosterBytes.write(data, 0, len); } - @Override - public boolean onTouchEvent(MotionEvent ev) { - // TODO: implement properly (i.e. detect double tap) - if (mIsFullscreen || !isPlaying()) { - return super.onTouchEvent(ev); + public void endData() { + if (mStatusCode == 200) { + if (mPosterBytes.size() > 0) { + Bitmap poster = BitmapFactory.decodeByteArray( + mPosterBytes.toByteArray(), 0, mPosterBytes.size()); + if (poster != null) { + mProxy.doSetPoster(poster); + } + } + cleanup(); + } else if (mStatusCode >= 300 && mStatusCode < 400) { + // We have a redirect. + mUrl = mHeaders.getLocation(); + if (mUrl != null) { + mHandler.post(new Runnable() { + public void run() { + if (mRequestHandle != null) { + mRequestHandle.setupRedirect(mUrl, mStatusCode, + new HashMap<String, String>()); + } + } + }); + } } - playFullScreen(); - return true; } - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - savePlaybackPosition(); + public void certificate(SslCertificate certificate) { + // Don't care. + } + + public void error(int id, String description) { + cleanup(); + } + + public boolean handleSslErrorRequest(SslError error) { + // Don't care. If this happens, data() will never be called so + // mPosterBytes will never be created, so no need to call cleanup. + return false; + } + // Tears down the poster bytes stream. Called on network thread. + private void cleanup() { + if (mPosterBytes != null) { + try { + mPosterBytes.close(); + } catch (IOException ignored) { + // Ignored. + } finally { + mPosterBytes = null; + } + } + } + + // Queue management methods. Called on WebCore thread. + private void retainQueue() { + if (mRequestQueue == null) { + mRequestQueue = new RequestQueue(mProxy.getContext()); + } + mQueueRefCount++; + } + + private void releaseQueue() { + if (mQueueRefCount == 0) { + return; + } + if (--mQueueRefCount == 0) { + mRequestQueue.shutdown(); + mRequestQueue = null; + } } } /** * Private constructor. - * @param context is the application context. + * @param webView is the WebView that hosts the video. + * @param nativePtr is the C++ pointer to the MediaPlayerPrivate object. */ - private HTML5VideoViewProxy(WebView webView) { + private HTML5VideoViewProxy(WebView webView, int nativePtr) { // This handler is for the main (UI) thread. super(Looper.getMainLooper()); // Save the WebView object. mWebView = webView; + // Save the native ptr + mNativePointer = nativePtr; + // create the message handler for this thread + createWebCoreHandler(); } - @Override - public void handleMessage(Message msg) { - // This executes on the UI thread. - switch (msg.what) { - case INIT: - // Create the video view and set a default controller. - mVideoView = new HTML5VideoView(mWebView.getContext()); - // This is needed because otherwise there will be a black square - // stuck on the screen. - mVideoView.setWillNotDraw(false); - mVideoView.setMediaController(new MediaController(mWebView.getContext())); - mChildView.mView = mVideoView; - break; - case PLAY: - if (mVideoView == null) { - return; + private void createWebCoreHandler() { + mWebCoreHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case PREPARED: { + Map<String, Object> map = (Map<String, Object>) msg.obj; + Integer duration = (Integer) map.get("dur"); + Integer width = (Integer) map.get("width"); + Integer height = (Integer) map.get("height"); + nativeOnPrepared(duration.intValue(), width.intValue(), + height.intValue(), mNativePointer); + break; + } + case ENDED: + nativeOnEnded(mNativePointer); + break; } - HashMap<String, Object> map = - (HashMap<String, Object>) msg.obj; - String url = (String) map.get("url"); - mVideoView.setVideoURI(Uri.parse(url)); - mVideoView.start(); - break; + } + }; + } + + private void doSetPoster(Bitmap poster) { + if (poster == null) { + return; } + // Send the bitmap over to the UI thread. + Message message = obtainMessage(SET_POSTER); + message.obj = poster; + sendMessage(message); + } + + public Context getContext() { + return mWebView.getContext(); } + // The public methods below are all called from WebKit only. /** * Play a video stream. * @param url is the URL of the video stream. - * @param webview is the WebViewCore that is requesting the playback. */ public void play(String url) { - // We need to know the webview that is requesting the playback. + if (url == null) { + return; + } Message message = obtainMessage(PLAY); - HashMap<String, Object> map = new HashMap(); - map.put("url", url); - message.obj = map; + message.obj = url; + sendMessage(message); + } + + /** + * Seek into the video stream. + * @param time is the position in the video stream. + */ + public void seek(int time) { + Message message = obtainMessage(SEEK); + message.obj = new Integer(time); + sendMessage(message); + } + + /** + * Pause the playback. + */ + public void pause() { + Message message = obtainMessage(PAUSE); sendMessage(message); } + /** + * Create the child view that will cary the poster. + */ public void createView() { mChildView = mWebView.mViewManager.createView(); sendMessage(obtainMessage(INIT)); } + /** + * Attach the poster view. + * @param x, y are the screen coordinates where the poster should be hung. + * @param width, height denote the size of the poster. + */ public void attachView(int x, int y, int width, int height) { if (mChildView == null) { return; @@ -213,11 +430,36 @@ class HTML5VideoViewProxy extends Handler { mChildView.attachView(x, y, width, height); } + /** + * Remove the child view and, thus, the poster. + */ public void removeView() { if (mChildView == null) { return; } mChildView.removeView(); + // This is called by the C++ MediaPlayerPrivate dtor. + // Cancel any active poster download. + if (mPosterDownloader != null) { + mPosterDownloader.cancelAndReleaseQueue(); + } + } + + /** + * Load the poster image. + * @param url is the URL of the poster image. + */ + public void loadPoster(String url) { + if (url == null) { + return; + } + // Cancel any active poster download. + if (mPosterDownloader != null) { + mPosterDownloader.cancelAndReleaseQueue(); + } + // Load the poster asynchronously + mPosterDownloader = new PosterDownloader(url, this); + mPosterDownloader.start(); } /** @@ -226,7 +468,10 @@ class HTML5VideoViewProxy extends Handler { * * @return a new HTML5VideoViewProxy object. */ - public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) { - return new HTML5VideoViewProxy(webViewCore.getWebView()); + public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore, int nativePtr) { + return new HTML5VideoViewProxy(webViewCore.getWebView(), nativePtr); } + + private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); + private native void nativeOnEnded(int nativePointer); } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 95b3a12..39a2470 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -16,6 +16,8 @@ package android.webkit; +import com.android.internal.widget.EditableInputConnection; + import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -38,6 +40,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; @@ -346,6 +349,16 @@ import java.util.ArrayList; @Override protected void onSelectionChanged(int selStart, int selEnd) { + // This code is copied from TextView.onDraw(). That code does not get + // executed, however, because the WebTextView does not draw, allowing + // webkit's drawing to show through. + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null && imm.isActive(this)) { + Spannable sp = (Spannable) getText(); + int candStart = EditableInputConnection.getComposingSpanStart(sp); + int candEnd = EditableInputConnection.getComposingSpanEnd(sp); + imm.updateSelection(this, selStart, selEnd, candStart, candEnd); + } if (!mFromWebKit && mWebView != null) { if (DebugFlags.WEB_TEXT_VIEW) { Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart @@ -430,18 +443,26 @@ import java.util.ArrayList; mGotTouchDown = true; break; case MotionEvent.ACTION_MOVE: + int slop = ViewConfiguration.get(mContext).getScaledTouchSlop(); Spannable buffer = getText(); int initialScrollX = Touch.getInitialScrollX(this, buffer); int initialScrollY = Touch.getInitialScrollY(this, buffer); super.onTouchEvent(event); - if (mScrollX != initialScrollX - || mScrollY != initialScrollY) { + if (Math.abs(mScrollX - initialScrollX) > slop + || Math.abs(mScrollY - initialScrollY) > slop) { if (mWebView != null) { mWebView.scrollFocusedTextInput(mScrollX, mScrollY); } mScrolled = true; return true; } + if (Math.abs((int) event.getX() - mDragStartX) < slop + && Math.abs((int) event.getY() - mDragStartY) < slop) { + // If the user has not scrolled further than slop, we should not + // send the drag. Instead, do nothing, and when the user lifts + // their finger, we will change the selection. + return true; + } if (mWebView != null) { // Only want to set the initial state once. if (!mDragSent) { diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4ca17ac..a5536dd 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -420,6 +420,7 @@ public class WebView extends AbsoluteLayout private static final int STD_SPEED = 480; // pixels per second // time for the longest scroll animation private static final int MAX_DURATION = 750; // milliseconds + private static final int SLIDE_TITLE_DURATION = 300; // milliseconds private Scroller mScroller; private boolean mWrapContent; @@ -2416,10 +2417,18 @@ public class WebView extends AbsoluteLayout if ((dx | dy) == 0) { return false; } - - if (true && animate) { + // mobile sites prefer to scroll to (0, 1), thus the + 1 below + boolean slideTitle = getVisibleTitleHeight() > 0 + && y <= getTitleHeight() + 1; + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "pinScrollTo slideTitle=" + slideTitle + + " getVisibleTitleHeight()=" + getVisibleTitleHeight() + + " animationDuration=" + animationDuration + " y=" + y); + } + if (slideTitle || animate) { // Log.d(LOGTAG, "startScroll: " + dx + " " + dy); - + if (slideTitle && animationDuration < SLIDE_TITLE_DURATION) + animationDuration = SLIDE_TITLE_DURATION; mScroller.startScroll(mScrollX, mScrollY, dx, dy, animationDuration > 0 ? animationDuration : computeDuration(dx, dy)); invalidate(); @@ -2754,7 +2763,8 @@ public class WebView extends AbsoluteLayout zoomScale = mZoomScale; // set mZoomScale to be 0 as we have done animation mZoomScale = 0; - animateZoom = false; // inform drawContentPicture we're done + // call invalidate() again to draw with the final filters + invalidate(); if (mNeedToAdjustWebTextView) { mNeedToAdjustWebTextView = false; mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, @@ -2899,6 +2909,8 @@ public class WebView extends AbsoluteLayout getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (isTextView) { + if (mWebTextView == null) return; + imm.showSoftInput(mWebTextView, 0); // Now we need to fake a touch event to place the cursor where the // user touched. diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 4566c4c..953dd92 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -1325,7 +1325,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe final int maxHeight = mPopup.getMaxAvailableHeight( getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations); - if (mDropDownAlwaysVisible) { + if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { // getMaxAvailableHeight() subtracts the padding, so we put it back, // to get the available height for the whole window int padding = 0; diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java index fe01866..4ec597c 100644 --- a/core/java/com/android/internal/widget/ContactHeaderWidget.java +++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java @@ -42,6 +42,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; +import android.widget.FasttrackBadgeWidget; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -64,15 +65,13 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList private TextView mDisplayNameView; private TextView mPhoneticNameView; private CheckBox mStarredView; - private ImageView mPhotoView; + private FasttrackBadgeWidget mPhotoView; private ImageView mPresenceView; private TextView mStatusView; private int mNoPhotoResource; private QueryHandler mQueryHandler; - protected long mContactId; protected Uri mContactUri; - protected Uri mStatusUri; protected String[] mExcludeMimes = null; @@ -94,6 +93,8 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList Contacts.STARRED, Contacts.PHOTO_ID, Contacts.PRESENCE_STATUS, + Contacts._ID, + Contacts.LOOKUP_KEY, }; protected static final int HEADER_DISPLAY_NAME_COLUMN_INDEX = 0; //TODO: We need to figure out how we're going to get the phonetic name. @@ -101,6 +102,8 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList protected static final int HEADER_STARRED_COLUMN_INDEX = 1; protected static final int HEADER_PHOTO_ID_COLUMN_INDEX = 2; protected static final int HEADER_PRESENCE_STATUS_COLUMN_INDEX = 3; + protected static final int HEADER_CONTACT_ID_COLUMN_INDEX = 4; + protected static final int HEADER_LOOKUP_KEY_COLUMN_INDEX = 5; //Projection used for finding the most recent social status. protected static final String[] SOCIAL_PROJECTION = new String[] { @@ -113,18 +116,29 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList //Projection used for looking up contact id from phone number protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] { PhoneLookup._ID, + PhoneLookup.LOOKUP_KEY, }; protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0; + protected static final int PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1; //Projection used for looking up contact id from email address protected static final String[] EMAIL_LOOKUP_PROJECTION = new String[] { RawContacts.CONTACT_ID, + Contacts.LOOKUP_KEY, }; protected static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0; + protected static final int EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX = 1; + protected static final String[] CONTACT_LOOKUP_PROJECTION = new String[] { + Contacts._ID, + }; + protected static final int CONTACT_LOOKUP_ID_COLUMN_INDEX = 0; private static final int TOKEN_CONTACT_INFO = 0; private static final int TOKEN_SOCIAL = 1; + private static final int TOKEN_PHONE_LOOKUP = 2; + private static final int TOKEN_EMAIL_LOOKUP = 3; + private static final int TOKEN_LOOKUP_CONTACT_FOR_SOCIAL_QUERY = 4; public ContactHeaderWidget(Context context) { this(context, null); @@ -151,8 +165,7 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList mStarredView = (CheckBox)findViewById(R.id.star); mStarredView.setOnClickListener(this); - mPhotoView = (ImageView)findViewById(R.id.photo); - mPhotoView.setOnClickListener(this); + mPhotoView = (FasttrackBadgeWidget) findViewById(R.id.photo); mPhotoView.setOnLongClickListener(this); mPresenceView = (ImageView) findViewById(R.id.presence); @@ -217,12 +230,46 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { try{ - if (token == TOKEN_CONTACT_INFO) { - bindContactInfo(cursor); - invalidate(); - } else if (token == TOKEN_SOCIAL) { - bindSocial(cursor); - invalidate(); + switch (token) { + case TOKEN_CONTACT_INFO: { + bindContactInfo(cursor); + invalidate(); + break; + } + case TOKEN_SOCIAL: { + bindSocial(cursor); + invalidate(); + break; + } + case TOKEN_PHONE_LOOKUP: { + if (cursor != null && cursor.moveToFirst()) { + long contactId = cursor.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX); + String lookupKey = cursor.getString( + PHONE_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX); + bindFromContactUri(Contacts.getLookupUri(contactId, lookupKey)); + } else { + setDisplayName((String) cookie, null); + } + break; + } + case TOKEN_EMAIL_LOOKUP: { + if (cursor != null && cursor.moveToFirst()) { + long contactId = cursor.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX); + String lookupKey = cursor.getString( + EMAIL_LOOKUP_CONTACT_LOOKUP_KEY_COLUMN_INDEX); + bindFromContactUri(Contacts.getLookupUri(contactId, lookupKey)); + } else { + setDisplayName((String) cookie, null); + } + break; + } + case TOKEN_LOOKUP_CONTACT_FOR_SOCIAL_QUERY: { + if (cursor != null && cursor.moveToFirst()) { + long contactId = cursor.getLong(CONTACT_LOOKUP_ID_COLUMN_INDEX); + startSocialQuery(ContentUris.withAppendedId( + Activities.CONTENT_CONTACT_STATUS_URI, contactId)); + } + } } } finally { if (cursor != null) { @@ -300,33 +347,31 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList * Convenience method for binding all available data from an existing * contact. * - * @param contactId the contact id of the contact whose info should be displayed. + * @param conatctUri a {Contacts.CONTENT_LOOKUP_URI} style URI. */ - public void bindFromContactId(long contactId) { - mContactId = contactId; - mContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mContactId); + public void bindFromContactLookupUri(Uri contactLookupUri) { + mContactUri = contactLookupUri; - bindContactUri(mContactUri); - bindSocialUri(ContentUris.withAppendedId(Activities.CONTENT_CONTACT_STATUS_URI, mContactId)); - } + // Query for the contactId so we can do the social query. + mQueryHandler.startQuery(TOKEN_LOOKUP_CONTACT_FOR_SOCIAL_QUERY, null, contactLookupUri, + CONTACT_LOOKUP_PROJECTION, null, null, null); - /** - * Convenience method for binding {@link Contacts} header details from a - * {@link Contacts#CONTENT_URI} reference. - */ - public void bindContactUri(Uri contactUri) { - mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, contactUri, HEADER_PROJECTION, - null, null, null); + startContactQuery(contactLookupUri); } /** - * Convenience method for binding {@link Activities} header details from a - * {@link Activities#CONTENT_CONTACT_STATUS_URI}. + * Convenience method for binding all available data from an existing + * contact. + * + * @param conatctUri a {Contacts.CONTENT_URI} style URI. */ - public void bindSocialUri(Uri contactSocial) { - mStatusUri = contactSocial; - mQueryHandler.startQuery(TOKEN_SOCIAL, null, mStatusUri, SOCIAL_PROJECTION, null, null, - null); + public void bindFromContactUri(Uri contactUri) { + mContactUri = contactUri; + long contactId = ContentUris.parseId(contactUri); + + startContactQuery(contactUri); + startSocialQuery(ContentUris.withAppendedId( + Activities.CONTENT_CONTACT_STATUS_URI, contactId)); } /** @@ -338,21 +383,9 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList * address, one of them will be chosen to bind to. */ public void bindFromEmail(String emailAddress) { - Cursor c = null; - try { - c = mContentResolver.query(Uri.withAppendedPath(Email.CONTENT_FILTER_EMAIL_URI, Uri - .encode(emailAddress)), EMAIL_LOOKUP_PROJECTION, null, null, null); - if (c != null && c.moveToFirst()) { - long contactId = c.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX); - bindFromContactId(contactId); - } else { - setDisplayName(emailAddress, null); - } - } finally { - if (c != null) { - c.close(); - } - } + mQueryHandler.startQuery(TOKEN_EMAIL_LOOKUP, emailAddress, + Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(emailAddress)), + EMAIL_LOOKUP_PROJECTION, null, null, null); } /** @@ -364,22 +397,19 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList * number, one of them will be chosen to bind to. */ public void bindFromPhoneNumber(String number) { - Cursor c = null; - try { - c = mContentResolver.query( - Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)), - PHONE_LOOKUP_PROJECTION, null, null, null); - if (c != null && c.moveToFirst()) { - long contactId = c.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX); - bindFromContactId(contactId); - } else { - setDisplayName(number, null); - } - } finally { - if (c != null) { - c.close(); - } - } + mQueryHandler.startQuery(TOKEN_PHONE_LOOKUP, number, + Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number), + PHONE_LOOKUP_PROJECTION, null, null, null); + } + + private void startSocialQuery(Uri contactSocial) { + mQueryHandler.startQuery(TOKEN_SOCIAL, null, contactSocial, SOCIAL_PROJECTION, null, null, + null); + } + + private void startContactQuery(Uri contactUri) { + mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, contactUri, HEADER_PROJECTION, + null, null, null); } /** @@ -390,6 +420,8 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList // TODO: Bring back phonetic name final String displayName = c.getString(HEADER_DISPLAY_NAME_COLUMN_INDEX); + final long contactId = c.getLong(HEADER_CONTACT_ID_COLUMN_INDEX); + final String lookupKey = c.getString(HEADER_LOOKUP_KEY_COLUMN_INDEX); final String phoneticName = null; this.setDisplayName(displayName, null); @@ -402,6 +434,7 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList photoBitmap = loadPlaceholderPhoto(null); } mPhotoView.setImageBitmap(photoBitmap); + mPhotoView.assignContactUri(Contacts.getLookupUri(contactId, lookupKey)); //Set the presence status int presence = c.getInt(HEADER_PRESENCE_STATUS_COLUMN_INDEX); @@ -423,27 +456,11 @@ public class ContactHeaderWidget extends FrameLayout implements View.OnClickList return; } - switch (view.getId()) { - case R.id.star: { - // Toggle "starred" state - final ContentValues values = new ContentValues(1); - values.put(Contacts.STARRED, mStarredView.isChecked()); - mContentResolver.update(mContactUri, values, null, null); - break; - } - case R.id.photo: { - // Photo launches contact detail action - final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, mContactUri); - final Rect target = getTargetRect(view); - intent.putExtra(Intents.EXTRA_TARGET_RECT, target); - intent.putExtra(Intents.EXTRA_MODE, Intents.MODE_SMALL); - if (mExcludeMimes != null) { - // Exclude specific MIME-types when requested - intent.putExtra(Intents.EXTRA_EXCLUDE_MIMES, mExcludeMimes); - } - mContext.startActivity(intent); - break; - } + if (view.getId() == R.id.star) { + // Toggle "starred" state + final ContentValues values = new ContentValues(1); + values.put(Contacts.STARRED, mStarredView.isChecked()); + mContentResolver.update(mContactUri, values, null, null); } } diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java index 7b940c9..aff92b8 100644 --- a/core/java/com/android/internal/widget/RotarySelector.java +++ b/core/java/com/android/internal/widget/RotarySelector.java @@ -19,8 +19,6 @@ package com.android.internal.widget; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Vibrator; import android.util.AttributeSet; @@ -28,7 +26,7 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.AccelerateInterpolator; - +import static android.view.animation.AnimationUtils.currentAnimationTimeMillis; import com.android.internal.R; @@ -69,7 +67,7 @@ public class RotarySelector extends View { private boolean mAnimating = false; private long mAnimationEndTime; private int mAnimatingDelta; - AccelerateInterpolator mInterpolator; + private AccelerateInterpolator mInterpolator; /** * True after triggering an action if the user of {@link OnDialTriggerListener} wants to @@ -96,12 +94,6 @@ public class RotarySelector extends View { private static final long VIBRATE_SHORT = 60; // msec private static final long VIBRATE_LONG = 100; // msec - // Various tweakable layout or behavior parameters: - - // How close to the edge of the screen, we let the handle get before - // triggering an action: - private static final int EDGE_THRESHOLD_DIP = 70; - /** * The drawable for the arrows need to be scrunched this many dips towards the rotary bg below * it. @@ -122,6 +114,11 @@ public class RotarySelector extends View { private static final boolean DRAW_CENTER_DIMPLE = false; + public RotarySelector(Context context) { + this(context, null); + } + + /** * Constructor used when this widget is created from a layout file. */ @@ -132,8 +129,6 @@ public class RotarySelector extends View { Resources r = getResources(); mDensity = r.getDisplayMetrics().density; if (DBG) log("- Density: " + mDensity); - // Density is 1.0 on HVGA (like Dream), and 1.5 on WVGA. - // Usage: raw_pixel_value = (int) (dpi_value * mDensity + 0.5f) // Assets (all are BitmapDrawables). mBackground = r.getDrawable(R.drawable.jog_dial_bg_cropped); @@ -143,6 +138,15 @@ public class RotarySelector extends View { mArrowLongRight = r.getDrawable(R.drawable.jog_dial_arrow_long_right_red); mArrowShortLeftAndRight = r.getDrawable(R.drawable.jog_dial_arrow_short_left_and_right); + // Arrows: + // All arrow assets are the same size (they're the full width of + // the screen) regardless of which arrows are actually visible. + int arrowW = mArrowShortLeftAndRight.getIntrinsicWidth(); + int arrowH = mArrowShortLeftAndRight.getIntrinsicHeight(); + mArrowShortLeftAndRight.setBounds(0, 0, arrowW, arrowH); + mArrowLongLeft.setBounds(0, 0, arrowW, arrowH); + mArrowLongRight.setBounds(0, 0, arrowW, arrowH); + mInterpolator = new AccelerateInterpolator(); } @@ -237,7 +241,7 @@ public class RotarySelector extends View { // update animating state before we draw anything if (mAnimating && !mFrozen) { - long millisLeft = mAnimationEndTime - System.currentTimeMillis(); + long millisLeft = mAnimationEndTime - currentAnimationTimeMillis(); if (DBG) log("millisleft for animating: " + millisLeft); if (millisLeft <= 0) { reset(); @@ -259,11 +263,6 @@ public class RotarySelector extends View { if (DBG) log(" Background BOUNDS: " + mBackground.getBounds()); mBackground.draw(canvas); - // Arrows: - // All arrow assets are the same size (they're the full width of - // the screen) regardless of which arrows are actually visible. - int arrowW = mArrowShortLeftAndRight.getIntrinsicWidth(); - int arrowH = mArrowShortLeftAndRight.getIntrinsicHeight(); // Draw the correct arrow(s) depending on the current state: Drawable currentArrow; @@ -280,7 +279,6 @@ public class RotarySelector extends View { default: throw new IllegalStateException("invalid mGrabbedState: " + mGrabbedState); } - currentArrow.setBounds(0, 0, arrowW, arrowH); currentArrow.draw(canvas); // debug: draw circle that should match the outer arc (good sanity check) @@ -382,63 +380,70 @@ public class RotarySelector extends View { final int eventX = (int) event.getX(); final int hitWindow = mDimple.getIntrinsicWidth(); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (DBG) log("touch-down"); - mTriggered = false; - if (mGrabbedState != RotarySelector.NOTHING_GRABBED) { - reset(); - invalidate(); - } - if (eventX < mLeftHandleX + hitWindow) { - mTouchDragOffset = eventX - mLeftHandleX; - mGrabbedState = RotarySelector.LEFT_HANDLE_GRABBED; - invalidate(); - vibrate(VIBRATE_SHORT); - } else if (eventX > mRightHandleX - hitWindow) { - mTouchDragOffset = eventX - mRightHandleX; - mGrabbedState = RotarySelector.RIGHT_HANDLE_GRABBED; - invalidate(); - vibrate(VIBRATE_SHORT); - } - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (DBG) log("touch-move"); - if (mGrabbedState == RotarySelector.LEFT_HANDLE_GRABBED) { - mTouchDragOffset = eventX - mLeftHandleX; - invalidate(); - if (eventX >= mRightHandleX - EDGE_PADDING_DIP && !mTriggered) { - mTriggered = true; - mFrozen = dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE); + final int action = event.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + if (DBG) log("touch-down"); + mTriggered = false; + if (mGrabbedState != NOTHING_GRABBED) { + reset(); + invalidate(); } - } else if (mGrabbedState == RotarySelector.RIGHT_HANDLE_GRABBED) { - mTouchDragOffset = eventX - mRightHandleX; - invalidate(); - if (eventX <= mLeftHandleX + EDGE_PADDING_DIP && !mTriggered) { - mTriggered = true; - mFrozen = dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE); + if (eventX < mLeftHandleX + hitWindow) { + mTouchDragOffset = eventX - mLeftHandleX; + mGrabbedState = LEFT_HANDLE_GRABBED; + invalidate(); + vibrate(VIBRATE_SHORT); + } else if (eventX > mRightHandleX - hitWindow) { + mTouchDragOffset = eventX - mRightHandleX; + mGrabbedState = RIGHT_HANDLE_GRABBED; + invalidate(); + vibrate(VIBRATE_SHORT); } - } - } else if ((event.getAction() == MotionEvent.ACTION_UP)) { - if (DBG) log("touch-up"); - // handle animating back to start if they didn't trigger - if (mGrabbedState == RotarySelector.LEFT_HANDLE_GRABBED - && Math.abs(eventX - mLeftHandleX) > 5) { - mAnimating = true; - mAnimationEndTime = System.currentTimeMillis() + ANIMATION_DURATION_MILLIS; - mAnimatingDelta = eventX - mLeftHandleX; - } else if (mGrabbedState == RotarySelector.RIGHT_HANDLE_GRABBED - && Math.abs(eventX - mRightHandleX) > 5) { - mAnimating = true; - mAnimationEndTime = System.currentTimeMillis() + ANIMATION_DURATION_MILLIS; - mAnimatingDelta = eventX - mRightHandleX; - } + break; - mTouchDragOffset = 0; - mGrabbedState = RotarySelector.NOTHING_GRABBED; - invalidate(); - } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { - if (DBG) log("touch-cancel"); - reset(); - invalidate(); + case MotionEvent.ACTION_MOVE: + if (DBG) log("touch-move"); + if (mGrabbedState == LEFT_HANDLE_GRABBED) { + mTouchDragOffset = eventX - mLeftHandleX; + invalidate(); + if (eventX >= mRightHandleX - EDGE_PADDING_DIP && !mTriggered) { + mTriggered = true; + mFrozen = dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE); + } + } else if (mGrabbedState == RIGHT_HANDLE_GRABBED) { + mTouchDragOffset = eventX - mRightHandleX; + invalidate(); + if (eventX <= mLeftHandleX + EDGE_PADDING_DIP && !mTriggered) { + mTriggered = true; + mFrozen = dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE); + } + } + break; + case MotionEvent.ACTION_UP: + if (DBG) log("touch-up"); + // handle animating back to start if they didn't trigger + if (mGrabbedState == LEFT_HANDLE_GRABBED + && Math.abs(eventX - mLeftHandleX) > 5) { + mAnimating = true; + mAnimationEndTime = currentAnimationTimeMillis() + ANIMATION_DURATION_MILLIS; + mAnimatingDelta = eventX - mLeftHandleX; + } else if (mGrabbedState == RIGHT_HANDLE_GRABBED + && Math.abs(eventX - mRightHandleX) > 5) { + mAnimating = true; + mAnimationEndTime = currentAnimationTimeMillis() + ANIMATION_DURATION_MILLIS; + mAnimatingDelta = eventX - mRightHandleX; + } + + mTouchDragOffset = 0; + mGrabbedState = NOTHING_GRABBED; + invalidate(); + break; + case MotionEvent.ACTION_CANCEL: + if (DBG) log("touch-cancel"); + reset(); + invalidate(); + break; } return true; } @@ -446,7 +451,7 @@ public class RotarySelector extends View { private void reset() { mAnimating = false; mTouchDragOffset = 0; - mGrabbedState = RotarySelector.NOTHING_GRABBED; + mGrabbedState = NOTHING_GRABBED; mTriggered = false; } diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index a185d8d..ba13519 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -133,23 +133,13 @@ static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) { LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, -1, c_path, - "org.bluez.AudioSink", "Connect", - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(path, c_path); + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.AudioSink", "Connect", + DBUS_TYPE_INVALID); - if (!reply && dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); - return JNI_FALSE; - } else if (!reply) { - LOGE("DBus reply is NULL in function %s", __FUNCTION__); - return JNI_FALSE; - } - return JNI_TRUE; + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; } #endif return JNI_FALSE; @@ -161,23 +151,13 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object, LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, -1, c_path, - "org.bluez.AudioSink", "Disconnect", - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(path, c_path); + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.AudioSink", "Disconnect", + DBUS_TYPE_INVALID); - if (!reply && dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); - return JNI_FALSE; - } else if (!reply) { - LOGE("DBus reply is NULL in function %s", __FUNCTION__); - return JNI_FALSE; - } - return JNI_TRUE; + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; } #endif return JNI_FALSE; diff --git a/core/res/res/drawable-hdpi/ic_emergency.png b/core/res/res/drawable-hdpi/ic_emergency.png Binary files differindex a2dd372..b4465ff 100644 --- a/core/res/res/drawable-hdpi/ic_emergency.png +++ b/core/res/res/drawable-hdpi/ic_emergency.png diff --git a/core/res/res/drawable-hdpi/title_bar_medium.png b/core/res/res/drawable-hdpi/title_bar_medium.png Binary files differnew file mode 100644 index 0000000..c13dd26 --- /dev/null +++ b/core/res/res/drawable-hdpi/title_bar_medium.png diff --git a/core/res/res/drawable-mdpi/title_bar_medium.png b/core/res/res/drawable-mdpi/title_bar_medium.png Binary files differnew file mode 100644 index 0000000..9d01f79 --- /dev/null +++ b/core/res/res/drawable-mdpi/title_bar_medium.png diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml index 8d7e470..e800dfa 100644 --- a/core/res/res/layout/contact_header.xml +++ b/core/res/res/layout/contact_header.xml @@ -19,17 +19,17 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:background="@drawable/title_bar_tall" + android:background="@drawable/title_bar_medium" android:paddingRight="5dip" android:gravity="center_vertical"> - <ImageView android:id="@+id/photo" - android:layout_width="56dip" - android:layout_height="62dip" + <android.widget.FasttrackBadgeWidget android:id="@+id/photo" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" android:layout_marginRight="10dip" android:layout_marginLeft="10dip" - android:scaleType="fitCenter" - android:background="@drawable/fasttrack_badge_middle_large"/> + style="@*android:style/Widget.FasttrackBadgeWidget.WindowSmall" /> + /> <LinearLayout android:layout_width="0dip" diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index 929d0a2..6dd2949 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -21,7 +21,8 @@ the user how to unlock their device, or make an emergency call. This is the portrait layout. --> -<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient + xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" @@ -34,31 +35,70 @@ android:layout_width="0dip" android:layout_height="fill_parent" android:layout_weight="1.0" + android:gravity="center_horizontal" > + <TextView + android:id="@+id/carrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="5dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + /> + <TextView + android:id="@+id/centerDot" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + /> + <TextView + android:id="@+id/time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="5dip" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="35sp" + /> + <TextView + android:id="@+id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_marginTop="-12dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + /> - <!-- lock icon next to header text --> - <LinearLayout - android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:layout_marginTop="3dip" - android:gravity="center" - > - <ImageView android:id="@+id/unlockLockIcon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginRight="3dip" - android:baselineAligned="true" - android:gravity="center" - android:src="@android:drawable/ic_lock_idle_lock" - /> - <TextView android:id="@+id/headerText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:textSize="18sp"/> - </LinearLayout> + <View + android:id="@+id/divider" + android:layout_width="fill_parent" + android:layout_height="1dip" + android:layout_centerHorizontal="true" + android:background="@android:drawable/divider_horizontal_dark" + /> + + <!-- lock icon and header message --> + <TextView + android:id="@+id/headerText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="12dip" + android:layout_centerHorizontal="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + android:drawableLeft="@drawable/ic_lock_idle_lock" + android:drawablePadding="4dip" + android:gravity="center" + /> <!-- fill space between header and button below --> @@ -82,7 +122,7 @@ <Button android:id="@+id/emergencyCallAlone" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:text="@android:string/lockscreen_emergency_call" + android:text="@string/lockscreen_emergency_call" android:textSize="14sp" android:drawableLeft="@drawable/ic_emergency" android:drawablePadding="8dip" @@ -105,7 +145,7 @@ <Button android:id="@+id/emergencyCallTogether" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:text="@android:string/lockscreen_emergency_call" + android:text="@string/lockscreen_emergency_call" android:textSize="14sp" android:drawableLeft="@drawable/ic_emergency" android:drawablePadding="8dip" @@ -115,7 +155,7 @@ </LinearLayout> <View - android:background="@android:drawable/code_lock_left" + android:background="@drawable/code_lock_left" android:layout_width="2dip" android:layout_height="fill_parent" /> @@ -124,4 +164,4 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" /> -</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> +</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
\ No newline at end of file diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index a6c31b6..a5d44fc 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -23,54 +23,93 @@ <com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" - android:layout_width="wrap_content" + android:layout_width="fill_parent" android:layout_height="fill_parent" + android:gravity="center_horizontal" android:background="#A0000000" > - <!-- lock icon and header message --> <LinearLayout + android:id="@+id/carrierAndDate" android:orientation="horizontal" - android:layout_width="fill_parent" - android:layout_height="0dip" - android:layout_weight="1.0" - android:gravity="center" - > - - <ImageView android:id="@+id/unlockLockIcon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="13dip" + > + <TextView + android:id="@+id/carrier" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginRight="6dip" - android:baselineAligned="true" - android:gravity="center" - android:src="@android:drawable/ic_lock_idle_lock" - /> - - <TextView android:id="@+id/headerText" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + /> + <TextView + android:id="@+id/centerDot" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="center" - android:textSize="18sp"/> + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + /> + <TextView + android:id="@+id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + /> </LinearLayout> + <TextView + android:id="@+id/time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_marginTop="-9dip" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="48sp" + /> + <View - android:background="@android:drawable/code_lock_top" - android:layout_width="fill_parent" - android:layout_height="2dip" /> - <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:id="@+id/divider" + android:layout_width="fill_parent" + android:layout_height="1dip" + android:layout_marginTop="-4dip" + android:layout_centerHorizontal="true" + android:background="@android:drawable/divider_horizontal_dark" + /> + + <!-- lock icon and header message --> + <TextView + android:id="@+id/headerText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="3dip" + android:layout_centerHorizontal="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textColor="?android:attr/textColorSecondary" + android:textSize="17sp" + android:drawableLeft="@drawable/ic_lock_idle_lock" + android:drawablePadding="4dip" + /> + + <com.android.internal.widget.LockPatternView + android:id="@+id/lockPattern" + android:layout_width="fill_parent" + android:layout_height="0dip" + android:layout_weight="1" + android:layout_marginTop="2dip" /> - <View - android:background="@android:drawable/code_lock_bottom" - android:layout_width="fill_parent" - android:layout_height="8dip" /> <!-- footer --> <FrameLayout android:layout_width="fill_parent" - android:layout_height="0dip" - android:layout_weight="1.0" + android:layout_height="wrap_content" > <!-- option 1: a single emergency call button --> @@ -82,8 +121,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" - android:text="@android:string/lockscreen_emergency_call" - android:textSize="14sp" + android:text="@string/lockscreen_emergency_call" + android:textAppearance="?android:attr/textAppearanceSmall" android:drawableLeft="@drawable/ic_emergency" android:drawablePadding="8dip" /> @@ -105,8 +144,8 @@ android:layout_marginBottom="4dip" android:layout_marginLeft="4dip" android:layout_marginRight="2dip" - android:text="@android:string/lockscreen_emergency_call" - android:textSize="14sp" + android:text="@string/lockscreen_emergency_call" + android:textAppearance="?android:attr/textAppearanceSmall" android:drawableLeft="@drawable/ic_emergency" android:drawablePadding="8dip" /> @@ -118,7 +157,7 @@ android:layout_marginBottom="4dip" android:layout_marginLeft="2dip" android:layout_marginRight="4dip" - android:textSize="14sp" + android:textAppearance="?android:attr/textAppearanceSmall" android:visibility="invisible" /> </LinearLayout> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a1a179b..eae838a 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -530,6 +530,11 @@ lines, the IME should provide multiple lines if it can. Corresponds to {@link android.text.InputType#TYPE_TEXT_FLAG_IME_MULTI_LINE}. --> <flag name="textImeMultiLine" value="0x00040001" /> + <!-- Can be combined with <var>text</var> and its variations to + indicate that the IME should not show any + dictionary-based word suggestions. Corresponds to + {@link android.text.InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS}. --> + <flag name="textNoSuggestions" value="0x00080001" /> <!-- Text that will be used as a URI. Corresponds to {@link android.text.InputType#TYPE_CLASS_TEXT} | {@link android.text.InputType#TYPE_TEXT_VARIATION_URI}. --> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 62a230c..c967c4c 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -18,7 +18,7 @@ */ --> <resources> - <drawable name="screen_background_light">#fff9f9f9</drawable> + <drawable name="screen_background_light">#ffffffff</drawable> <drawable name="screen_background_dark">#ff202020</drawable> <drawable name="status_bar_closed_default_background">#ff000000</drawable> <drawable name="status_bar_opened_default_background">#ff000000</drawable> @@ -45,7 +45,7 @@ <color name="dim_foreground_dark_inverse">#323232</color> <color name="dim_foreground_dark_inverse_disabled">#80323232</color> <color name="hint_foreground_dark">#808080</color> - <color name="background_light">#fff9f9f9</color> + <color name="background_light">#ffffffff</color> <color name="bright_foreground_light">#ff000000</color> <color name="bright_foreground_light_inverse">#ffffffff</color> <color name="bright_foreground_light_disabled">#80000000</color> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 69ddd63..bd79c75 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1962,7 +1962,7 @@ <string name="allow">Allow</string> <string name="deny">Deny</string> <string name="permission_request_notification_title">Permission Requested</string> - <string name="permission_request_notification_subtitle">for account <xliff:g id="account" example="foo@gmail.com">%s</xliff:g></string> + <string name="permission_request_notification_with_subtitle">Permission Requested\nfor account <xliff:g id="account" example="foo@gmail.com">%s</xliff:g></string> <!-- Label to show for a service that is running because it is an input method. --> <string name="input_method_binding_label">Input method</string> diff --git a/data/etc/Android.mk b/data/etc/Android.mk index a32d8ea..041c5d3 100644 --- a/data/etc/Android.mk +++ b/data/etc/Android.mk @@ -32,3 +32,20 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) + +######################## +include $(CLEAR_VARS) + +LOCAL_MODULE := required_hardware.xml + +LOCAL_MODULE_TAGS := user + +LOCAL_MODULE_CLASS := ETC + +# This will install the file in /system/etc/permissions +# +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions + +LOCAL_SRC_FILES := $(LOCAL_MODULE) + +include $(BUILD_PREBUILT) diff --git a/data/etc/android.hardware.camera.flash-autofocus.xml b/data/etc/android.hardware.camera.flash-autofocus.xml new file mode 100644 index 0000000..55f1900 --- /dev/null +++ b/data/etc/android.hardware.camera.flash-autofocus.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard set of features for a camera with a flash. Note + that this currently requires having auto-focus as well. --> +<permissions> + <feature name="android.hardware.camera" /> + <feature name="android.hardware.camera.autofocus" /> + <feature name="android.hardware.camera.flash" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.light.xml b/data/etc/android.hardware.sensor.light.xml new file mode 100644 index 0000000..78b0fec --- /dev/null +++ b/data/etc/android.hardware.sensor.light.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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 + + 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. +--> + +<!-- Feature for devices with an ambient light sensor. --> +<permissions> + <feature name="android.hardware.sensor.light" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.proximity.xml b/data/etc/android.hardware.sensor.proximity.xml new file mode 100644 index 0000000..d1948de --- /dev/null +++ b/data/etc/android.hardware.sensor.proximity.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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 + + 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. +--> + +<!-- Feature for devices with a proximity sensor. --> +<permissions> + <feature name="android.hardware.sensor.proximity" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml new file mode 100644 index 0000000..72e0485 --- /dev/null +++ b/data/etc/android.hardware.telephony.cdma.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard set of features for a CDMA phone. --> +<permissions> + <feature name="android.hardware.telephony" /> + <feature name="android.hardware.telephony.cdma" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml new file mode 100644 index 0000000..ffde433 --- /dev/null +++ b/data/etc/android.hardware.telephony.gsm.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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 + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard set of features for a GSM phone. --> +<permissions> + <feature name="android.hardware.telephony" /> + <feature name="android.hardware.telephony.gsm" /> +</permissions> diff --git a/data/etc/required_hardware.xml b/data/etc/required_hardware.xml new file mode 100644 index 0000000..896a148 --- /dev/null +++ b/data/etc/required_hardware.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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 + + 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. +--> + +<!-- These are the hardware features that all devices must possess. + These are always added for you by the build system, you do not need + to add them yourself. --> +<permissions> + <feature name="android.hardware.sensor.compass" /> + <feature name="android.hardware.sensor.accelerometer" /> + <feature name="android.hardware.bluetooth" /> + <feature name="android.hardware.wifi" /> +</permissions> diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 3266f1e..c6f57d4 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -36,7 +36,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { private Drawable mCurrDrawable; private int mAlpha = 0xFF; private ColorFilter mColorFilter; - private boolean mDither = DEFAULT_DITHER; private int mCurIndex = -1; private boolean mMutated; @@ -83,10 +82,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { @Override public void setDither(boolean dither) { - if (mDither != dither) { - mDither = dither; + if (mDrawableContainerState.mDither != dither) { + mDrawableContainerState.mDither = dither; if (mCurrDrawable != null) { - mCurrDrawable.setDither(mDither); + mCurrDrawable.setDither(mDrawableContainerState.mDither); } } } @@ -212,7 +211,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { if (d != null) { d.setVisible(isVisible(), true); d.setAlpha(mAlpha); - d.setDither(mDither); + d.setDither(mDrawableContainerState.mDither); d.setColorFilter(mColorFilter); d.setState(getState()); d.setLevel(getLevel()); @@ -285,6 +284,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { boolean mCanConstantState; boolean mPaddingChecked = false; + + boolean mDither = DEFAULT_DITHER; DrawableContainerState(DrawableContainerState orig, DrawableContainer owner, Resources res) { @@ -323,6 +324,8 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { mOpacity = orig.mOpacity; mHaveStateful = orig.mHaveStateful; mStateful = orig.mStateful; + + mDither = orig.mDither; } else { mDrawables = new Drawable[10]; diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index 89db4fa..d35c5e3 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -140,6 +140,8 @@ public class RenderScript { native void nScriptSetTimeZone(int script, byte[] timeZone); native void nScriptSetType(int type, boolean writable, String name, int slot); native void nScriptSetRoot(boolean isRoot); + native void nScriptSetInvokable(String name, int slot); + native void nScriptInvoke(int id, int slot); native void nScriptCBegin(); native void nScriptCSetScript(byte[] script, int offset, int length); diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java index a402471..35791a3 100644 --- a/graphics/java/android/renderscript/Script.java +++ b/graphics/java/android/renderscript/Script.java @@ -25,6 +25,22 @@ public class Script extends BaseObj { boolean mIsRoot; Type[] mTypes; boolean[] mWritable; + Invokable[] mInvokables; + + public static class Invokable { + RenderScript mRS; + Script mScript; + int mSlot; + String mName; + + Invokable() { + mSlot = -1; + } + + public void execute() { + mRS.nScriptInvoke(mScript.mID, mSlot); + } + } Script(int id, RenderScript rs) { super(rs); @@ -61,12 +77,15 @@ public class Script extends BaseObj { Type[] mTypes; String[] mNames; boolean[] mWritable; + int mInvokableCount = 0; + Invokable[] mInvokables; Builder(RenderScript rs) { mRS = rs; mTypes = new Type[MAX_SLOT]; mNames = new String[MAX_SLOT]; mWritable = new boolean[MAX_SLOT]; + mInvokables = new Invokable[MAX_SLOT]; } public void setType(Type t, int slot) { @@ -79,6 +98,15 @@ public class Script extends BaseObj { mNames[slot] = name; } + public Invokable addInvokable(String func) { + Invokable i = new Invokable(); + i.mName = func; + i.mRS = mRS; + i.mSlot = mInvokableCount; + mInvokables[mInvokableCount++] = i; + return i; + } + public void setType(boolean writable, int slot) { mWritable[slot] = writable; } @@ -90,11 +118,20 @@ public class Script extends BaseObj { mRS.nScriptSetType(mTypes[ct].mID, mWritable[ct], mNames[ct], ct); } } + for(int ct=0; ct < mInvokableCount; ct++) { + mRS.nScriptSetInvokable(mInvokables[ct].mName, ct); + } } void transferObject(Script s) { s.mIsRoot = mIsRoot; s.mTypes = mTypes; + s.mInvokables = new Invokable[mInvokableCount]; + for(int ct=0; ct < mInvokableCount; ct++) { + s.mInvokables[ct] = mInvokables[ct]; + s.mInvokables[ct].mScript = s; + } + s.mInvokables = null; } public void setRoot(boolean r) { diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 90b5958..eae6f24 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -891,6 +891,29 @@ nScriptSetType(JNIEnv *_env, jobject _this, jint type, jboolean writable, jstrin } static void +nScriptSetInvoke(JNIEnv *_env, jobject _this, jstring _str, jint slot) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nScriptSetInvoke, con(%p)", con); + const char* n = NULL; + if (_str) { + n = _env->GetStringUTFChars(_str, NULL); + } + rsScriptSetInvoke(con, n, slot); + if (n) { + _env->ReleaseStringUTFChars(_str, n); + } +} + +static void +nScriptInvoke(JNIEnv *_env, jobject _this, jint obj, jint slot) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nScriptInvoke, con(%p), script(%p)", con, (void *)obj); + rsScriptInvoke(con, (RsScript)obj, slot); +} + +static void nScriptSetRoot(JNIEnv *_env, jobject _this, jboolean isRoot) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); @@ -1366,6 +1389,8 @@ static JNINativeMethod methods[] = { {"nScriptSetTimeZone", "(I[B)V", (void*)nScriptSetTimeZone }, {"nScriptSetType", "(IZLjava/lang/String;I)V", (void*)nScriptSetType }, {"nScriptSetRoot", "(Z)V", (void*)nScriptSetRoot }, +{"nScriptSetInvokable", "(Ljava/lang/String;I)V", (void*)nScriptSetInvoke }, +{"nScriptInvoke", "(II)V", (void*)nScriptInvoke }, {"nScriptCBegin", "()V", (void*)nScriptCBegin }, {"nScriptCSetScript", "([BII)V", (void*)nScriptCSetScript }, diff --git a/include/private/ui/SharedBufferStack.h b/include/private/ui/SharedBufferStack.h index 8b0f154..c02b2e7 100644 --- a/include/private/ui/SharedBufferStack.h +++ b/include/private/ui/SharedBufferStack.h @@ -69,12 +69,6 @@ class SharedClient; // ---------------------------------------------------------------------------- -struct FlatRegion { // 12 bytes - static const unsigned int NUM_RECT_MAX = 1; - uint32_t count; - uint16_t rects[4*NUM_RECT_MAX]; -}; - // should be 128 bytes (32 longs) class SharedBufferStack { @@ -84,6 +78,18 @@ class SharedBufferStack friend class SharedBufferServer; public: + struct FlatRegion { // 12 bytes + static const unsigned int NUM_RECT_MAX = 1; + uint32_t count; + uint16_t rects[4*NUM_RECT_MAX]; + }; + + struct Statistics { // 4 longs + typedef int32_t usecs_t; + usecs_t totalTime; + usecs_t reserved[3]; + }; + SharedBufferStack(); void init(int32_t identity); status_t setDirtyRegion(int buffer, const Region& reg); @@ -100,7 +106,8 @@ public: volatile int32_t reallocMask; int32_t identity; // surface's identity (const) - int32_t reserved32[13]; + int32_t reserved32[9]; + Statistics stats; FlatRegion dirtyRegion[NUM_BUFFER_MAX]; // 12*4=48 bytes }; @@ -223,7 +230,7 @@ public: status_t queue(int buf); bool needNewBuffer(int buffer) const; status_t setDirtyRegion(int buffer, const Region& reg); - + private: friend struct Condition; friend struct DequeueCondition; @@ -257,6 +264,8 @@ private: }; int32_t tail; + // statistics... + nsecs_t mDequeueTime[NUM_BUFFER_MAX]; }; // ---------------------------------------------------------------------------- @@ -275,6 +284,9 @@ public: Region getDirtyRegion(int buffer) const; + SharedBufferStack::Statistics getStats() const; + + private: struct UnlockUpdate : public UpdateBase { const int lockedBuffer; diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java index ee80014..9923011 100644 --- a/keystore/java/android/security/ServiceCommand.java +++ b/keystore/java/android/security/ServiceCommand.java @@ -49,8 +49,6 @@ public class ServiceCommand { public static final int BUFFER_LENGTH = 4096; - private static final boolean DBG = true; - private String mServiceName; private String mTag; private InputStream mIn; @@ -61,7 +59,6 @@ public class ServiceCommand { if (mSocket != null) { return true; } - if (DBG) Log.d(mTag, "connecting..."); try { mSocket = new LocalSocket(); @@ -80,7 +77,6 @@ public class ServiceCommand { } private void disconnect() { - if (DBG) Log.d(mTag,"disconnecting..."); try { if (mSocket != null) mSocket.close(); } catch (IOException ex) { } diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index ac2e738..87ad97c 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -297,6 +297,16 @@ ScriptSetType { param const char * name } +ScriptSetInvoke { + param const char * name + param uint32_t slot + } + +ScriptInvoke { + param RsScript s + param uint32_t slot + } + ScriptSetRoot { param bool isRoot } diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp index fde31a1..99a085d 100644 --- a/libs/rs/rsScript.cpp +++ b/libs/rs/rsScript.cpp @@ -27,6 +27,8 @@ Script::Script() mEnviroment.mClearColor[2] = 0; mEnviroment.mClearColor[3] = 1; mEnviroment.mClearDepth = 1; + mEnviroment.mClearStencil = 0; + mEnviroment.mIsRoot = false; } Script::~Script() @@ -83,10 +85,23 @@ void rsi_ScriptSetType(Context * rsc, RsType vt, uint32_t slot, bool writable, c } } +void rsi_ScriptSetInvoke(Context *rsc, const char *name, uint32_t slot) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mInvokableNames[slot] = name; +} + +void rsi_ScriptInvoke(Context *rsc, RsScript vs, uint32_t slot) +{ + Script *s = static_cast<Script *>(vs); + s->mEnviroment.mInvokables[slot](); +} + + void rsi_ScriptSetRoot(Context * rsc, bool isRoot) { ScriptCState *ss = &rsc->mScriptC; - ss->mEnviroment.mIsRoot = isRoot; + ss->mScript->mEnviroment.mIsRoot = isRoot; } diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h index 60f83a6..e40531e 100644 --- a/libs/rs/rsScript.h +++ b/libs/rs/rsScript.h @@ -34,6 +34,7 @@ class ProgramFragmentStore; class Script : public ObjectBase { public: + typedef void (* InvokeFunc_t)(void); Script(); virtual ~Script(); @@ -52,17 +53,22 @@ public: ObjectBaseRef<ProgramFragment> mFragment; //ObjectBaseRef<ProgramRaster> mRaster; ObjectBaseRef<ProgramFragmentStore> mFragmentStore; - + InvokeFunc_t mInvokables[MAX_SCRIPT_BANKS]; + const char * mScriptText; + uint32_t mScriptTextLength; }; Enviroment_t mEnviroment; uint32_t mCounstantBufferCount; + ObjectBaseRef<Allocation> mSlots[MAX_SCRIPT_BANKS]; ObjectBaseRef<const Type> mTypes[MAX_SCRIPT_BANKS]; String8 mSlotNames[MAX_SCRIPT_BANKS]; bool mSlotWritable[MAX_SCRIPT_BANKS]; + + virtual bool run(Context *, uint32_t launchID) = 0; }; diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 8230cbc..108ae5a 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -82,36 +82,27 @@ bool ScriptC::run(Context *rsc, uint32_t launchIndex) ScriptCState::ScriptCState() { + mScript = NULL; clear(); } ScriptCState::~ScriptCState() { - if (mAccScript) { - accDeleteScript(mAccScript); - } + delete mScript; + mScript = NULL; } void ScriptCState::clear() { - memset(&mProgram, 0, sizeof(mProgram)); - for (uint32_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) { mConstantBufferTypes[ct].clear(); mSlotNames[ct].setTo(""); + mInvokableNames[ct].setTo(""); mSlotWritable[ct] = false; } - memset(&mEnviroment, 0, sizeof(mEnviroment)); - mEnviroment.mClearColor[0] = 0; - mEnviroment.mClearColor[1] = 0; - mEnviroment.mClearColor[2] = 0; - mEnviroment.mClearColor[3] = 1; - mEnviroment.mClearDepth = 1; - mEnviroment.mClearStencil = 0; - mEnviroment.mIsRoot = false; - - mAccScript = NULL; + delete mScript; + mScript = new ScriptC(); mInt32Defines.clear(); mFloatDefines.clear(); @@ -127,9 +118,9 @@ static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) return NULL; } -void ScriptCState::runCompiler(Context *rsc) +void ScriptCState::runCompiler(Context *rsc, ScriptC *s) { - mAccScript = accCreateScript(); + s->mAccScript = accCreateScript(); String8 tmp; rsc->appendNameDefines(&tmp); @@ -139,44 +130,51 @@ void ScriptCState::runCompiler(Context *rsc) appendTypes(&tmp); tmp.append("#line 1\n"); - const char* scriptSource[] = {tmp.string(), mProgram.mScriptText}; - int scriptLength[] = {tmp.length(), mProgram.mScriptTextLength} ; - accScriptSource(mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength); - accRegisterSymbolCallback(mAccScript, symbolLookup, NULL); - accCompileScript(mAccScript); - accGetScriptLabel(mAccScript, "main", (ACCvoid**) &mProgram.mScript); - accGetScriptLabel(mAccScript, "init", (ACCvoid**) &mProgram.mInit); - rsAssert(mProgram.mScript); + const char* scriptSource[] = {tmp.string(), s->mEnviroment.mScriptText}; + int scriptLength[] = {tmp.length(), s->mEnviroment.mScriptTextLength} ; + accScriptSource(s->mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength); + accRegisterSymbolCallback(s->mAccScript, symbolLookup, NULL); + accCompileScript(s->mAccScript); + accGetScriptLabel(s->mAccScript, "main", (ACCvoid**) &s->mProgram.mScript); + accGetScriptLabel(s->mAccScript, "init", (ACCvoid**) &s->mProgram.mInit); + rsAssert(s->mProgram.mScript); - if (!mProgram.mScript) { + if (!s->mProgram.mScript) { ACCchar buf[4096]; ACCsizei len; - accGetScriptInfoLog(mAccScript, sizeof(buf), &len, buf); + accGetScriptInfoLog(s->mAccScript, sizeof(buf), &len, buf); LOGE(buf); } - if (mProgram.mInit) { - mProgram.mInit(); + if (s->mProgram.mInit) { + s->mProgram.mInit(); } for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { if (mSlotNames[ct].length() > 0) { - accGetScriptLabel(mAccScript, + accGetScriptLabel(s->mAccScript, mSlotNames[ct].string(), - (ACCvoid**) &mProgram.mSlotPointers[ct]); - LOGE("var %s %p", mSlotNames[ct].string(), mProgram.mSlotPointers[ct]); + (ACCvoid**) &s->mProgram.mSlotPointers[ct]); + } + } + + for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { + if (mInvokableNames[ct].length() > 0) { + accGetScriptLabel(s->mAccScript, + mInvokableNames[ct].string(), + (ACCvoid**) &s->mEnviroment.mInvokables[ct]); } } - mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); - mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); - mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore()); + s->mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); + s->mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); + s->mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore()); - if (mProgram.mScript) { + if (s->mProgram.mScript) { const static int pragmaMax = 16; ACCsizei pragmaCount; ACCchar * str[pragmaMax]; - accGetPragmas(mAccScript, &pragmaCount, pragmaMax, &str[0]); + accGetPragmas(s->mAccScript, &pragmaCount, pragmaMax, &str[0]); for (int ct=0; ct < pragmaCount; ct+=2) { if (!strcmp(str[ct], "version")) { @@ -188,12 +186,12 @@ void ScriptCState::runCompiler(Context *rsc) continue; } if (!strcmp(str[ct+1], "parent")) { - mEnviroment.mVertex.clear(); + s->mEnviroment.mVertex.clear(); continue; } ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]); if (pv != NULL) { - mEnviroment.mVertex.set(pv); + s->mEnviroment.mVertex.set(pv); continue; } LOGE("Unreconized value %s passed to stateVertex", str[ct+1]); @@ -208,12 +206,12 @@ void ScriptCState::runCompiler(Context *rsc) continue; } if (!strcmp(str[ct+1], "parent")) { - mEnviroment.mFragment.clear(); + s->mEnviroment.mFragment.clear(); continue; } ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]); if (pf != NULL) { - mEnviroment.mFragment.set(pf); + s->mEnviroment.mFragment.set(pf); continue; } LOGE("Unreconized value %s passed to stateFragment", str[ct+1]); @@ -224,13 +222,13 @@ void ScriptCState::runCompiler(Context *rsc) continue; } if (!strcmp(str[ct+1], "parent")) { - mEnviroment.mFragmentStore.clear(); + s->mEnviroment.mFragmentStore.clear(); continue; } ProgramFragmentStore * pfs = (ProgramFragmentStore *)rsc->lookupName(str[ct+1]); if (pfs != NULL) { - mEnviroment.mFragmentStore.set(pfs); + s->mEnviroment.mFragmentStore.set(pfs); continue; } LOGE("Unreconized value %s passed to stateFragmentStore", str[ct+1]); @@ -351,33 +349,6 @@ void ScriptCState::appendTypes(String8 *str) s.append(";\n"); LOGD(s); str->append(s); -#if 0 - for (size_t ct2=0; ct2 < e->getComponentCount(); ct2++) { - const Component *c = e->getComponent(ct2); - tmp.setTo("#define "); - tmp.append(mSlotNames[ct]); - tmp.append("_"); - tmp.append(c->getComponentName()); - switch (c->getType()) { - case Component::FLOAT: - tmp.append(" loadF("); - break; - case Component::SIGNED: - sprintf(buf, " loadI%i(", c->getBits()); - tmp.append(buf); - break; - case Component::UNSIGNED: - sprintf(buf, " loadU%i(", c->getBits()); - tmp.append(buf); - break; - } - sprintf(buf, "%i, %i)\n", ct, ct2); - tmp.append(buf); - - LOGD(tmp); - str->append(tmp); - } -#endif } } } @@ -394,15 +365,16 @@ void rsi_ScriptCBegin(Context * rsc) void rsi_ScriptCSetScript(Context * rsc, void *vp) { - ScriptCState *ss = &rsc->mScriptC; - ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp); + rsAssert(0); + //ScriptCState *ss = &rsc->mScriptC; + //ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp); } void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len) { ScriptCState *ss = &rsc->mScriptC; - ss->mProgram.mScriptText = text; - ss->mProgram.mScriptTextLength = len; + ss->mScript->mEnviroment.mScriptText = text; + ss->mScript->mEnviroment.mScriptTextLength = len; } @@ -410,14 +382,11 @@ RsScript rsi_ScriptCCreate(Context * rsc) { ScriptCState *ss = &rsc->mScriptC; - ss->runCompiler(rsc); + ScriptC *s = ss->mScript; + ss->mScript = NULL; - ScriptC *s = new ScriptC(); + ss->runCompiler(rsc, s); s->incUserRef(); - s->mAccScript = ss->mAccScript; - ss->mAccScript = NULL; - s->mEnviroment = ss->mEnviroment; - s->mProgram = ss->mProgram; for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { s->mTypes[ct].set(ss->mConstantBufferTypes[ct].get()); s->mSlotNames[ct] = ss->mSlotNames[ct]; diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index 8aa99ef..355f0c3 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -67,17 +67,15 @@ public: ScriptCState(); ~ScriptCState(); - ACCscript* mAccScript; - - ScriptC::Program_t mProgram; - Script::Enviroment_t mEnviroment; + ScriptC *mScript; ObjectBaseRef<const Type> mConstantBufferTypes[MAX_SCRIPT_BANKS]; String8 mSlotNames[MAX_SCRIPT_BANKS]; bool mSlotWritable[MAX_SCRIPT_BANKS]; + String8 mInvokableNames[MAX_SCRIPT_BANKS]; void clear(); - void runCompiler(Context *rsc); + void runCompiler(Context *rsc, ScriptC *s); void appendVarDefines(String8 *str); void appendTypes(String8 *str); diff --git a/libs/surfaceflinger/BlurFilter.cpp b/libs/surfaceflinger/BlurFilter.cpp index 5dc0ba0..1ffbd5b 100644 --- a/libs/surfaceflinger/BlurFilter.cpp +++ b/libs/surfaceflinger/BlurFilter.cpp @@ -111,6 +111,50 @@ struct BlurColor565 } }; +template <int FACTOR = 0> +struct BlurColor888X +{ + typedef uint32_t type; + int r, g, b; + inline BlurColor888X() { } + inline BlurColor888X(uint32_t v) { + v = BLUR_RGBA_TO_HOST(v); + r = v & 0xFF; + g = (v >> 8) & 0xFF; + b = (v >> 16) & 0xFF; + } + inline void clear() { r=g=b=0; } + inline uint32_t to(int shift, int last, int dither) const { + int R = r; + int G = g; + int B = b; + if (UNLIKELY(last)) { + if (FACTOR>0) { + int L = (R+G+G+B)>>2; + R += ((L - R) * FACTOR) >> 8; + G += ((L - G) * FACTOR) >> 8; + B += ((L - B) * FACTOR) >> 8; + } + } + R >>= shift; + G >>= shift; + B >>= shift; + return BLUR_HOST_TO_RGBA((0xFF<<24) | (B<<16) | (G<<8) | R); + } + inline BlurColor888X& operator += (const BlurColor888X& rhs) { + r += rhs.r; + g += rhs.g; + b += rhs.b; + return *this; + } + inline BlurColor888X& operator -= (const BlurColor888X& rhs) { + r -= rhs.r; + g -= rhs.g; + b -= rhs.b; + return *this; + } +}; + struct BlurGray565 { typedef uint16_t type; @@ -316,7 +360,13 @@ status_t blurFilter( int kernelSizeUser, int repeat) { - return blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat); + status_t err = BAD_VALUE; + if (image->format == GGL_PIXEL_FORMAT_RGB_565) { + err = blurFilter< BlurColor565<0x80> >(image, image, kernelSizeUser, repeat); + } else if (image->format == GGL_PIXEL_FORMAT_RGBX_8888) { + err = blurFilter< BlurColor888X<0x80> >(image, image, kernelSizeUser, repeat); + } + return err; } } // namespace android diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index d893f0a..cc913cb 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -174,6 +174,13 @@ void DisplayHardware::init(uint32_t dpy) surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); + if (mFlags & UPDATE_ON_DEMAND) { + // if we have update on demand, we definitely don't need to + // preserve the backbuffer, which is usually costly. + eglSurfaceAttrib(display, surface, + EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); + } + if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { if (dummy == EGL_BUFFER_PRESERVED) { mFlags |= BUFFER_PRESERVED; diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 1e7f1e6..7387c85 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -303,7 +303,6 @@ uint32_t Layer::doTransaction(uint32_t flags) // Index of the back buffer const bool backbufferChanged = (front.w != temp.w) || (front.h != temp.h); - if (backbufferChanged) { // the size changed, we need to ask our client to request a new buffer LOGD_IF(DEBUG_RESIZE, @@ -318,17 +317,6 @@ uint32_t Layer::doTransaction(uint32_t flags) // buffer, it'll get the new size. setDrawingSize(temp.w, temp.h); - // all buffers need reallocation - lcblk->reallocate(); - - // recompute the visible region - // FIXME: ideally we would do that only when we have received - // a buffer of the right size - flags |= Layer::eVisibleRegion; - this->contentDirty = true; - -#if 0 - // FIXME: handle freeze lock // we're being resized and there is a freeze display request, // acquire a freeze lock, so that the screen stays put // until we've redrawn at the new size; this is to avoid @@ -340,7 +328,12 @@ uint32_t Layer::doTransaction(uint32_t flags) mFreezeLock = mFlinger->getFreezeLock(); } } -#endif + + // recompute the visible region + flags |= Layer::eVisibleRegion; + this->contentDirty = true; + // all buffers need reallocation + lcblk->reallocate(); } if (temp.sequence != front.sequence) { @@ -382,6 +375,13 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) const Region dirty(lcblk->getDirtyRegion(buf)); mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); + + const Layer::State& front(drawingState()); + if (newFrontBuffer->getWidth() == front.w && + newFrontBuffer->getHeight() ==front.h) { + mFreezeLock.clear(); + } + // FIXME: signal an event if we have more buffers waiting // mFlinger->signalEvent(); diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index e14f35b..0ef663f 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -40,9 +40,9 @@ const char* const LayerBlur::typeID = "LayerBlur"; LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i) - : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), - mRefreshCache(true), mCacheAge(0), mTextureName(-1U), - mWidthScale(1.0f), mHeightScale(1.0f) +: LayerBaseClient(flinger, display, client, i), mCacheDirty(true), +mRefreshCache(true), mCacheAge(0), mTextureName(-1U), +mWidthScale(1.0f), mHeightScale(1.0f) { } @@ -136,6 +136,13 @@ void LayerBlur::onDraw(const Region& clip) const // create the texture name the first time // can't do that in the ctor, because it runs in another thread. glGenTextures(1, &mTextureName); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &mReadFormat); + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &mReadType); + if (mReadFormat != GL_RGB || mReadType != GL_UNSIGNED_SHORT_5_6_5) { + mReadFormat = GL_RGBA; + mReadType = GL_UNSIGNED_BYTE; + mBlurFormat = GGL_PIXEL_FORMAT_RGBX_8888; + } } Region::const_iterator it = clip.begin(); @@ -143,33 +150,39 @@ void LayerBlur::onDraw(const Region& clip) const if (it != end) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mTextureName); - + if (mRefreshCache) { mRefreshCache = false; mAutoRefreshPending = false; - - // allocate enough memory for 4-bytes (2 pixels) aligned data - const int32_t s = (w + 1) & ~1; - uint16_t* const pixels = (uint16_t*)malloc(s*h*2); + + int32_t pixelSize = 4; + int32_t s = w; + if (mReadType == GL_UNSIGNED_SHORT_5_6_5) { + // allocate enough memory for 4-bytes (2 pixels) aligned data + s = (w + 1) & ~1; + pixelSize = 2; + } + + uint16_t* const pixels = (uint16_t*)malloc(s*h*pixelSize); // This reads the frame-buffer, so a h/w GL would have to // finish() its rendering first. we don't want to do that // too often. Read data is 4-bytes aligned. - glReadPixels(X, Y, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); - + glReadPixels(X, Y, w, h, mReadFormat, mReadType, pixels); + // blur that texture. GGLSurface bl; bl.version = sizeof(GGLSurface); bl.width = w; bl.height = h; bl.stride = s; - bl.format = GGL_PIXEL_FORMAT_RGB_565; + bl.format = mBlurFormat; bl.data = (GGLubyte*)pixels; blurFilter(&bl, 8, 2); if (mFlags & (DisplayHardware::NPOT_EXTENSION)) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, w, h, 0, + mReadFormat, mReadType, pixels); mWidthScale = 1.0f / w; mHeightScale =-1.0f / h; mYOffset = 0; @@ -178,10 +191,10 @@ void LayerBlur::onDraw(const Region& clip) const GLuint th = 1 << (31 - clz(h)); if (tw < w) tw <<= 1; if (th < h) th <<= 1; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, mReadFormat, tw, th, 0, + mReadFormat, mReadType, NULL); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels); + mReadFormat, mReadType, pixels); mWidthScale = 1.0f / tw; mHeightScale =-1.0f / th; mYOffset = th-h; @@ -189,7 +202,7 @@ void LayerBlur::onDraw(const Region& clip) const free((void*)pixels); } - + const State& s = drawingState(); if (UNLIKELY(s.alpha < 0xFF)) { const GGLfixed alpha = (s.alpha << 16)/255; diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h index bf36ae4..2e9d7c6 100644 --- a/libs/surfaceflinger/LayerBlur.h +++ b/libs/surfaceflinger/LayerBlur.h @@ -59,6 +59,9 @@ private: mutable GLfloat mWidthScale; mutable GLfloat mHeightScale; mutable GLfloat mYOffset; + mutable GLint mReadFormat; + mutable GLint mReadType; + mutable uint32_t mBlurFormat; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index 38a897d..667571b 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -595,6 +595,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, *overlayRef = new OverlayRef(mOverlayHandle, channel, mWidth, mHeight, mFormat, mWidthStride, mHeightStride); + mLayer.mFlinger->signalEvent(); } LayerBuffer::OverlaySource::~OverlaySource() diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index a352431..31b5128 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -1521,6 +1521,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) /*** Layer ***/ sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get()); if (l != 0) { + SharedBufferStack::Statistics stats = l->lcblk->getStats(); result.append( l->lcblk->dump(" ") ); sp<const Buffer> buf0(l->getBuffer(0)); sp<const Buffer> buf1(l->getBuffer(1)); @@ -1539,10 +1540,10 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) snprintf(buffer, SIZE, " " "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," - " freezeLock=%p\n", + " freezeLock=%p, dq-q-time=%u us\n", l->pixelFormat(), w0, h0, s0, w1, h1, s1, - l->getFreezeLock().get()); + l->getFreezeLock().get(), stats.totalTime); result.append(buffer); buffer[0] = 0; } diff --git a/libs/ui/SharedBufferStack.cpp b/libs/ui/SharedBufferStack.cpp index 7789a3f..9ad4349 100644 --- a/libs/ui/SharedBufferStack.cpp +++ b/libs/ui/SharedBufferStack.cpp @@ -276,6 +276,8 @@ ssize_t SharedBufferClient::dequeue() LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", tail, stack.head, stack.available, stack.queued); } + + const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); //LOGD("[%d] about to dequeue a buffer", // mSharedStack->identity); @@ -296,6 +298,8 @@ ssize_t SharedBufferClient::dequeue() LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s", dequeued, tail, dump("").string()); + mDequeueTime[dequeued] = dequeueTime; + return dequeued; } @@ -312,7 +316,7 @@ status_t SharedBufferClient::undoDequeue(int buf) status_t SharedBufferClient::lock(int buf) { LockCondition condition(this, buf); - status_t err = waitForCondition(condition); + status_t err = waitForCondition(condition); return err; } @@ -321,6 +325,9 @@ status_t SharedBufferClient::queue(int buf) QueueUpdate update(this); status_t err = updateCondition( update ); LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); + SharedBufferStack& stack( *mSharedStack ); + const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); + stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); return err; } @@ -393,5 +400,12 @@ Region SharedBufferServer::getDirtyRegion(int buffer) const return stack.getDirtyRegion(buffer); } +SharedBufferStack::Statistics SharedBufferServer::getStats() const +{ + SharedBufferStack& stack( *mSharedStack ); + return stack.stats; +} + + // --------------------------------------------------------------------------- }; // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index ba4c4c7..c4c6149 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1148,6 +1148,13 @@ void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) { CODEC_LOGV("Finished flushing both ports, now continuing from" " seek-time."); + // Clear this flag in case the decoder sent us either + // the EVENT_BUFFER_FLAG(1) or an output buffer with + // the EOS flag set _while_ flushing. Since we're going + // to submit "fresh" input data now, this flag no longer + // applies to our future. + mNoMoreOutputData = false; + drainInputBuffers(); fillOutputBuffers(); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java index 69e93a1..10796f1 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java @@ -16,6 +16,7 @@ package com.android.mediaframeworktest.stress; + import com.android.mediaframeworktest.MediaFrameworkTest; import java.io.BufferedWriter; @@ -26,6 +27,7 @@ import java.io.Writer; import android.hardware.Camera; import android.media.MediaPlayer; import android.media.MediaRecorder; +import android.os.Looper; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -54,7 +56,14 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me private static final String OUTPUT_FILE_EXT = ".3gp"; private static final String MEDIA_STRESS_OUTPUT = "/sdcard/mediaStressOutput.txt"; - + private Looper mCameraLooper = null; + private Looper mRecorderLooper = null; + private final Object lock = new Object(); + private final Object recorderlock = new Object(); + private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000; // Milliseconds. + private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback(); + private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback(); + public MediaRecorderStressTest() { super("com.android.mediaframeworktest", MediaFrameworkTest.class); } @@ -63,41 +72,129 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me getActivity(); super.setUp(); } - + + private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback { + public void onError(int error, android.hardware.Camera camera) { + if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) { + assertTrue("Camera test mediaserver died", false); + } + } + } + + private final class RecorderErrorCallback implements MediaRecorder.OnErrorListener { + public void onError(MediaRecorder mr, int what, int extra) { + // fail the test case no matter what error come up + assertTrue("mediaRecorder error", false); + } + } + + private void initializeCameraMessageLooper() { + Log.v(TAG, "start looper"); + new Thread() { + @Override + public void run() { + // Set up a looper to be used by camera. + Looper.prepare(); + Log.v(TAG, "start loopRun"); + mCameraLooper = Looper.myLooper(); + mCamera = Camera.open(); + synchronized (lock) { + lock.notify(); + } + Looper.loop(); + Log.v(TAG, "initializeMessageLooper: quit."); + } + }.start(); + } + + private void initializeRecorderMessageLooper() { + Log.v(TAG, "start looper"); + new Thread() { + @Override + public void run() { + Looper.prepare(); + Log.v(TAG, "start loopRun"); + mRecorderLooper = Looper.myLooper(); + mRecorder = new MediaRecorder(); + synchronized (recorderlock) { + recorderlock.notify(); + } + Looper.loop(); // Blocks forever until Looper.quit() is called. + Log.v(TAG, "initializeMessageLooper: quit."); + } + }.start(); + } + + /* + * Terminates the message looper thread. + */ + private void terminateCameraMessageLooper() { + mCameraLooper.quit(); + try { + Thread.sleep(1000); + } catch (Exception e){ + Log.v(TAG, e.toString()); + } + mCamera.release(); + } + + /* + * Terminates the message looper thread. + */ + private void terminateRecorderMessageLooper() { + mRecorderLooper.quit(); + try { + Thread.sleep(1000); + } catch (Exception e){ + Log.v(TAG, e.toString()); + } + mRecorder.release(); + } + //Test case for stressing the camera preview. @LargeTest public void testStressCamera() throws Exception { - SurfaceHolder mSurfaceHolder; + SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); File stressOutFile = new File(MEDIA_STRESS_OUTPUT); Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); output.write("Camera start preview stress:\n"); - output.write("Total number of loops:" + + output.write("Total number of loops:" + NUMBER_OF_CAMERA_STRESS_LOOPS + "\n"); - try { + try { Log.v(TAG, "Start preview"); output.write("No of loop: "); + for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++){ - mCamera = Camera.open(); + synchronized (lock) { + initializeCameraMessageLooper(); + try { + lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE); + } catch(Exception e) { + Log.v(TAG, "wait was interrupted."); + } + } + mCamera.setErrorCallback(mCameraErrorCallback); mCamera.setPreviewDisplay(mSurfaceHolder); mCamera.startPreview(); Thread.sleep(WAIT_TIME_CAMERA_TEST); mCamera.stopPreview(); - mCamera.release(); + terminateCameraMessageLooper(); output.write(" ," + i); } } catch (Exception e) { - Log.v(TAG, e.toString()); + assertTrue("CameraStressTest", false); + Log.v(TAG, e.toString()); } output.write("\n\n"); output.close(); } - + //Test case for stressing the camera preview. @LargeTest public void testStressRecorder() throws Exception { String filename; - SurfaceHolder mSurfaceHolder; + SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); File stressOutFile = new File(MEDIA_STRESS_OUTPUT); Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); @@ -108,12 +205,20 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me output.write("No of loop: "); Log.v(TAG, "Start preview"); for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++){ + synchronized (recorderlock) { + initializeRecorderMessageLooper(); + try { + recorderlock.wait(WAIT_FOR_COMMAND_TO_COMPLETE); + } catch(Exception e) { + Log.v(TAG, "wait was interrupted."); + } + } Log.v(TAG, "counter = " + i); filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; Log.v(TAG, filename); - mRecorder = new MediaRecorder(); + mRecorder.setOnErrorListener(mRecorderErrorCallback); mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); - mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filename); mRecorder.setVideoFrameRate(20); mRecorder.setVideoSize(176,144); @@ -125,47 +230,63 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me Log.v(TAG, "prepare"); mRecorder.prepare(); Log.v(TAG, "before release"); - Thread.sleep(WAIT_TIME_RECORDER_TEST); + Thread.sleep(WAIT_TIME_RECORDER_TEST); mRecorder.reset(); - mRecorder.release(); + terminateRecorderMessageLooper(); output.write(", " + i); } } catch (Exception e) { - Log.v(TAG, e.toString()); + assertTrue("Recorder Stress test", false); + Log.v(TAG, e.toString()); } output.write("\n\n"); output.close(); } - - + //Stress test case for switching camera and video recorder preview. @LargeTest public void testStressCameraSwitchRecorder() throws Exception { String filename; - SurfaceHolder mSurfaceHolder; + SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); File stressOutFile = new File(MEDIA_STRESS_OUTPUT); Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); output.write("Camera and video recorder preview switching\n"); output.write("Total number of loops:" + NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n"); - try { + try { Log.v(TAG, "Start preview"); output.write("No of loop: "); for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++){ - mCamera = Camera.open(); + synchronized (lock) { + initializeCameraMessageLooper(); + try { + lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE); + } catch(Exception e) { + Log.v(TAG, "wait was interrupted."); + } + } + mCamera.setErrorCallback(mCameraErrorCallback); mCamera.setPreviewDisplay(mSurfaceHolder); mCamera.startPreview(); Thread.sleep(WAIT_TIME_CAMERA_TEST); mCamera.stopPreview(); - mCamera.release(); + terminateCameraMessageLooper(); mCamera = null; Log.v(TAG, "release camera"); filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; Log.v(TAG, filename); - mRecorder = new MediaRecorder(); + synchronized (recorderlock) { + initializeRecorderMessageLooper(); + try { + recorderlock.wait(WAIT_FOR_COMMAND_TO_COMPLETE); + } catch(Exception e) { + Log.v(TAG, "wait was interrupted."); + } + } + mRecorder.setOnErrorListener(mRecorderErrorCallback); mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); - mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filename); mRecorder.setVideoFrameRate(20); mRecorder.setVideoSize(176,144); @@ -176,23 +297,24 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me Log.v(TAG, "prepare"); mRecorder.prepare(); Log.v(TAG, "before release"); - Thread.sleep(WAIT_TIME_CAMERA_TEST); - mRecorder.release(); + Thread.sleep(WAIT_TIME_CAMERA_TEST); + terminateRecorderMessageLooper(); Log.v(TAG, "release video recorder"); output.write(", " + i); } } catch (Exception e) { + assertTrue("Camer and recorder switch mode", false); Log.v(TAG, e.toString()); } output.write("\n\n"); output.close(); } - + //Stress test case for record a video and play right away. @LargeTest public void testStressRecordVideoAndPlayback() throws Exception { String filename; - SurfaceHolder mSurfaceHolder; + SurfaceHolder mSurfaceHolder; mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder(); File stressOutFile = new File(MEDIA_STRESS_OUTPUT); Writer output = new BufferedWriter(new FileWriter(stressOutFile, true)); @@ -204,10 +326,18 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me for (int i = 0; i < NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS; i++){ filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT; Log.v(TAG, filename); - mRecorder = new MediaRecorder(); + synchronized (recorderlock) { + initializeRecorderMessageLooper(); + try { + recorderlock.wait(WAIT_FOR_COMMAND_TO_COMPLETE); + } catch(Exception e) { + Log.v(TAG, "wait was interrupted."); + } + } + mRecorder.setOnErrorListener(mRecorderErrorCallback); mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); - mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filename); mRecorder.setVideoFrameRate(20); mRecorder.setVideoSize(352,288); @@ -216,11 +346,11 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me Log.v(TAG, "mediaRecorder setPreview"); mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); mRecorder.prepare(); - mRecorder.start(); + mRecorder.start(); Thread.sleep(WAIT_TIME_RECORD); Log.v(TAG, "Before stop"); mRecorder.stop(); - mRecorder.release(); + terminateRecorderMessageLooper(); //start the playback MediaPlayer mp = new MediaPlayer(); mp.setDataSource(filename); @@ -232,10 +362,10 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me output.write(", " + i); } } catch (Exception e) { + assertTrue("record and playback", false); Log.v(TAG, e.toString()); } output.write("\n\n"); output.close(); - } + } } - diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp index 705794a..d4887ba 100644 --- a/opengl/tests/gl2_basic/gl2_basic.cpp +++ b/opengl/tests/gl2_basic/gl2_basic.cpp @@ -33,27 +33,72 @@ using namespace android; static void printGLString(const char *name, GLenum s) { + fprintf(stderr, "printGLString %s, %d\n", name, s); +#if 0 // causes hangs const char *v = (const char *)glGetString(s); - if (v) - printf("GL %s = %s\n", name, v); + int error = glGetError(); + fprintf(stderr, "glGetError() = %d, result of glGetString = %x\n", error, + (unsigned int)v); + if ((v < (const char*) 0) || (v > (const char*) 0x10000)) + fprintf(stderr, "GL %s = %s\n", name, v); else - printf("GL %s = (null)\n", name); + fprintf(stderr, "GL %s = (null) 0x%08x\n", name, (unsigned int) v); +#endif +} + +static const char* eglErrorToString[] = { + "EGL_SUCCESS", // 0x3000 12288 + "EGL_NOT_INITIALIZED", + "EGL_BAD_ACCESS", // 0x3002 12290 + "EGL_BAD_ALLOC", + "EGL_BAD_ATTRIBUTE", + "EGL_BAD_CONFIG", + "EGL_BAD_CONTEXT", // 0x3006 12294 + "EGL_BAD_CURRENT_SURFACE", + "EGL_BAD_DISPLAY", + "EGL_BAD_MATCH", + "EGL_BAD_NATIVE_PIXMAP", + "EGL_BAD_NATIVE_WINDOW", + "EGL_BAD_PARAMETER", // 0x300c 12300 + "EGL_BAD_SURFACE" +}; + +static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { + if (returnVal != EGL_TRUE) { + fprintf(stderr, "%s() returned %d\n", op, returnVal); + } + + for(EGLint error = eglGetError(); + error != EGL_SUCCESS; + error = eglGetError()) { + const char* errorString = "unknown"; + if (error >= EGL_SUCCESS && error <= EGL_BAD_SURFACE) { + errorString = eglErrorToString[error - EGL_SUCCESS]; + } + fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, + errorString, error); + } } int main(int argc, char** argv) { + EGLBoolean returnValue; + EGLConfig configs[2]; + EGLint config_count; + + EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; EGLint s_configAttribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, - EGL_NONE + EGL_BUFFER_SIZE, EGL_DONT_CARE, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_DEPTH_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE }; - EGLint numConfigs = -1; EGLint majorVersion; EGLint minorVersion; - EGLConfig config; EGLContext context; EGLSurface surface; EGLint w, h; @@ -63,19 +108,63 @@ int main(int argc, char** argv) EGLNativeWindowType window = 0; window = android_createDisplaySurface(); + checkEglError("<init>"); dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - eglInitialize(dpy, &majorVersion, &minorVersion); + checkEglError("eglGetDisplay"); + if (dpy == EGL_NO_DISPLAY) { + printf("eglGetDisplay returned EGL_NO_DISPLAY.\n"); + return 0; + } + returnValue = eglInitialize(dpy, &majorVersion, &minorVersion); + checkEglError("eglInitialize", returnValue); + fprintf(stderr, "EGL version %d.%d\n", majorVersion, minorVersion); + + returnValue = eglGetConfigs (dpy, configs, 2, &config_count); + checkEglError("eglGetConfigs", returnValue); + fprintf(stderr, "Config count: %d\n", config_count); + for(int i = 0; i < config_count; i++) { + fprintf(stderr, "%d: 0x%08x\n", i, (unsigned int) configs[i]); + } +#if 0 + EGLConfig config; EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &config); - surface = eglCreateWindowSurface(dpy, config, window, NULL); + checkEglError("EGLUtils::selectConfigForNativeWindow"); +#else + int chooseConfigResult = eglChooseConfig(dpy, s_configAttribs, configs, 2, &config_count); + checkEglError("eglChooseConfig", chooseConfigResult); + if (chooseConfigResult != EGL_TRUE )
+ {
+ printf("eglChooseConfig failed\n"); + return 0;
+ } +#endif - EGLint gl2_0Attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + surface = eglCreateWindowSurface(dpy, configs[0], window, NULL); + checkEglError("eglCreateWindowSurface"); + if (surface == EGL_NO_SURFACE) + { + printf("gelCreateWindowSurface failed.\n"); + return 0; + } + EGLint gl2_0Attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - context = eglCreateContext(dpy, config, NULL, gl2_0Attribs); + context = eglCreateContext(dpy, configs[0], EGL_NO_CONTEXT, context_attribs); + checkEglError("eglCreateContext"); + if (context == EGL_NO_CONTEXT) + { + printf("eglCreateContext failed\n"); + return 0; + } eglMakeCurrent(dpy, surface, surface, context); + checkEglError("eglMakeCurrent"); eglQuerySurface(dpy, surface, EGL_WIDTH, &w); + checkEglError("eglQuerySurface"); eglQuerySurface(dpy, surface, EGL_HEIGHT, &h); + checkEglError("eglQuerySurface"); GLint dim = w<h ? w : h; + fprintf(stderr, "Window dimensions: %d x %d\n", w, h); + printGLString("Version", GL_VERSION); printGLString("Vendor", GL_VENDOR); printGLString("Renderer", GL_RENDERER); diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 0e60dd6..c6be61d 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -649,11 +649,21 @@ class BackupManagerService extends IBackupManager.Stub { int N = packages.size(); for (int a = N-1; a >= 0; a--) { PackageInfo pkg = packages.get(a); - ApplicationInfo app = pkg.applicationInfo; - if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) - || app.backupAgentName == null - || (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA, - pkg.packageName) != PackageManager.PERMISSION_GRANTED)) { + try { + ApplicationInfo app = pkg.applicationInfo; + if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) + || app.backupAgentName == null + || (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA, + pkg.packageName) != PackageManager.PERMISSION_GRANTED)) { + packages.remove(a); + } + else { + // we will need the shared library path, so look that up and store it here + app = mPackageManager.getApplicationInfo(pkg.packageName, + PackageManager.GET_SHARED_LIBRARY_FILES); + pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles; + } + } catch (NameNotFoundException e) { packages.remove(a); } } diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 30c25e0..60195b9 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -16,6 +16,7 @@ package com.android.server; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.os.Handler; @@ -111,6 +112,30 @@ class DockObserver extends UEventObserver { Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); mContext.sendStickyBroadcast(intent); + + // Launch a dock activity + String category; + switch (mDockState) { + case Intent.EXTRA_DOCK_STATE_CAR: + category = Intent.CATEGORY_CAR_DOCK; + break; + case Intent.EXTRA_DOCK_STATE_DESK: + category = Intent.CATEGORY_DESK_DOCK; + break; + default: + category = null; + break; + } + if (category != null) { + intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(category); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, e.getCause()); + } + } } } }; diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index af60556..f6a1be7 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -1069,13 +1069,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return mIntent; } - boolean isInProximity(double latitude, double longitude) { + boolean isInProximity(double latitude, double longitude, float accuracy) { Location loc = new Location(""); loc.setLatitude(latitude); loc.setLongitude(longitude); double radius = loc.distanceTo(mLocation); - return radius <= mRadius; + return radius <= Math.max(mRadius,accuracy); } @Override @@ -1115,6 +1115,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run long now = System.currentTimeMillis(); double latitude = loc.getLatitude(); double longitude = loc.getLongitude(); + float accuracy = loc.getAccuracy(); ArrayList<PendingIntent> intentsToRemove = null; for (ProximityAlert alert : mProximityAlerts.values()) { @@ -1124,7 +1125,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if ((expiration == -1) || (now <= expiration)) { boolean entered = mProximitiesEntered.contains(alert); boolean inProximity = - alert.isInProximity(latitude, longitude); + alert.isInProximity(latitude, longitude, accuracy); if (!entered && inProximity) { if (LOCAL_LOGV) { Log.v(TAG, "Entered alert"); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 35f508b..84250bc 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -957,9 +957,6 @@ class PowerManagerService extends IPowerManager.Stub private void sendNotificationLocked(boolean on, int why) { - if (mProximitySensorActive) { - why = WindowManagerPolicy.OFF_BECAUSE_OF_PROXIMITY_SENSOR; - } if (!on) { mStillNeedSleepNotification = false; } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 60496d6..d53f002 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -7056,7 +7056,7 @@ public class WindowManagerService extends IWindowManager.Stub + " attHidden=" + mAttachedHidden + " tok.hiddenRequested=" + (mAppToken != null ? mAppToken.hiddenRequested : false) - + " tok.idden=" + + " tok.hidden=" + (mAppToken != null ? mAppToken.hidden : false) + " animating=" + mAnimating + " tok animating=" @@ -7085,10 +7085,20 @@ public class WindowManagerService extends IWindowManager.Stub if (mAttrs.type != TYPE_APPLICATION_STARTING && mAppToken != null) { mAppToken.firstWindowDrawn = true; - if (mAnimation == null && mAppToken.startingData != null) { + + if (mAppToken.startingData != null) { if (DEBUG_STARTING_WINDOW) Log.v(TAG, "Finish starting " + mToken + ": first real window is shown, no animation"); + // If this initial window is animating, stop it -- we + // will do an animation to reveal it from behind the + // starting window, so there is no need for it to also + // be doing its own stuff. + if (mAnimation != null) { + mAnimation = null; + // Make sure we clean up the animation. + mAnimating = true; + } mFinishedStarting.add(mAppToken); mH.sendEmptyMessage(H.FINISHED_STARTING); } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 4368464..2672c6d 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -129,6 +129,8 @@ public class PhoneNumberUtils return uri.getSchemeSpecificPart(); } + // TODO: We don't check for SecurityException here (requires + // READ_PHONE_STATE permission). if (scheme.equals("voicemail")) { return TelephonyManager.getDefault().getVoiceMailNumber(); } @@ -1179,6 +1181,35 @@ public class PhoneNumberUtils } /** + * isVoiceMailNumber: checks a given number against the voicemail + * number provided by the RIL and SIM card. The caller must have + * the READ_PHONE_STATE credential. + * + * @param number the number to look up. + * @return true if the number is in the list of voicemail. False + * otherwise, including if the caller does not have the permission + * to read the VM number. + * @hide TODO: pending API Council approval + */ + public static boolean isVoiceMailNumber(String number) { + String vmNumber; + + try { + vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(); + } catch (SecurityException ex) { + return false; + } + + // Strip the separators from the number before comparing it + // to the list. + number = extractNetworkPortion(number); + + // compare tolerates null so we need to make sure that we + // don't return true when both are null. + return !TextUtils.isEmpty(number) && compare(number, vmNumber); + } + + /** * Translates any alphabetic letters (i.e. [A-Za-z]) in the * specified phone number into the equivalent numeric digits, * according to the phone keypad letter mapping described in diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index c8490e9..01b1746 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -101,13 +101,12 @@ public class CallerInfo { public boolean isCachedPhotoCurrent; private boolean mIsEmergency; - - // Don't keep checking VM if it's going to throw an exception for this proc. - private static boolean sSkipVmCheck = false; + private boolean mIsVoiceMail; public CallerInfo() { // TODO: Move all the basic initialization here? mIsEmergency = false; + mIsVoiceMail = false; } /** @@ -220,32 +219,15 @@ public class CallerInfo { public static CallerInfo getCallerInfo(Context context, String number) { if (TextUtils.isEmpty(number)) { return null; - } else { - // Change the callerInfo number ONLY if it is an emergency number - // or if it is the voicemail number. If it is either, take a - // shortcut and skip the query. - if (PhoneNumberUtils.isEmergencyNumber(number)) { - return new CallerInfo().markAsEmergency(context); - } else { - try { - if (!sSkipVmCheck && PhoneNumberUtils.compare(number, - TelephonyManager.getDefault().getVoiceMailNumber())) { - CallerInfo ci = new CallerInfo(); - - // Note we're setting the phone number here (refer to javadoc - // comments at the top of CallerInfo class). - ci.phoneNumber = TelephonyManager.getDefault().getVoiceMailAlphaTag(); - // TODO: FIND ANOTHER ICON - //info.photoResource = android.R.drawable.badge_voicemail; - return ci; - } - } catch (SecurityException ex) { - // Don't crash if this process doesn't have permission to - // retrieve VM number. It's still allowed to look up caller info. - // But don't try it again. - sSkipVmCheck = true; - } - } + } + + // Change the callerInfo number ONLY if it is an emergency number + // or if it is the voicemail number. If it is either, take a + // shortcut and skip the query. + if (PhoneNumberUtils.isEmergencyNumber(number)) { + return new CallerInfo().markAsEmergency(context); + } else if (PhoneNumberUtils.isVoiceMailNumber(number)) { + return new CallerInfo().markAsVoiceMail(); } Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); @@ -304,6 +286,13 @@ public class CallerInfo { } /** + * @return true if the caller info is a voicemail number. + */ + public boolean isVoiceMailNumber() { + return mIsVoiceMail; + } + + /** * Mark this CallerInfo as an emergency call. * @param context To lookup the localized 'Emergency Number' string. * @return this instance. @@ -323,6 +312,37 @@ public class CallerInfo { return this; } + + /** + * Mark this CallerInfo as a voicemail call. The voicemail label + * is obtained from the telephony manager. Caller must hold the + * READ_PHONE_STATE permission otherwise the phoneNumber will be + * set to null. + * @return this instance. + */ + // TODO: As in the emergency number handling, we end up writing a + // string in the phone number field. + /* package */ CallerInfo markAsVoiceMail() { + mIsVoiceMail = true; + + try { + String voiceMailLabel = TelephonyManager.getDefault().getVoiceMailAlphaTag(); + + phoneNumber = voiceMailLabel; + } catch (SecurityException se) { + // Should never happen: if this process does not have + // permission to retrieve VM tag, it should not have + // permission to retrieve VM number and would not call + // this method. + // Leave phoneNumber untouched. + Log.e(TAG, "Cannot access VoiceMail.", se); + } + // TODO: There is no voicemail picture? + // FIXME: FIND ANOTHER ICON + // photoResource = android.R.drawable.badge_voicemail; + return this; + } + private static String normalize(String s) { if (s == null || s.length() > 0) { return s; @@ -330,4 +350,31 @@ public class CallerInfo { return null; } } + + /** + * @return a string debug representation of this instance. + */ + public String toString() { + return new StringBuilder(384) + .append("\nname: " + name) + .append("\nphoneNumber: " + phoneNumber) + .append("\ncnapName: " + cnapName) + .append("\nnumberPresentation: " + numberPresentation) + .append("\nnamePresentation: " + namePresentation) + .append("\ncontactExits: " + contactExists) + .append("\nphoneLabel: " + phoneLabel) + .append("\nnumberType: " + numberType) + .append("\nnumberLabel: " + numberLabel) + .append("\nphotoResource: " + photoResource) + .append("\nperson_id: " + person_id) + .append("\nneedUpdate: " + needUpdate) + .append("\ncontactRefUri: " + contactRefUri) + .append("\ncontactRingtoneUri: " + contactRefUri) + .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail) + .append("\ncachedPhoto: " + cachedPhoto) + .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent) + .append("\nemergency: " + mIsEmergency) + .append("\nvoicemail " + mIsVoiceMail) + .toString(); + } } diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java index 4227a84..802e79b 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java +++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java @@ -47,9 +47,6 @@ public class CallerInfoAsyncQuery { private CallerInfoAsyncQueryHandler mHandler; - // Don't keep checking VM if it's going to throw an exception for this proc. - private static boolean sSkipVmCheck = false; - /** * Interface for a CallerInfoAsyncQueryHandler result return. */ @@ -227,18 +224,7 @@ public class CallerInfoAsyncQuery { // comments at the top of CallerInfo class). mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext); } else if (cw.event == EVENT_VOICEMAIL_NUMBER) { - mCallerInfo = new CallerInfo(); - try { - // Note we're setting the phone number here (refer to javadoc - // comments at the top of CallerInfo class). - mCallerInfo.phoneNumber = - TelephonyManager.getDefault().getVoiceMailAlphaTag(); - } catch (SecurityException ex) { - // Should never happen: if this process does not have - // permission to retrieve VM tag, it should not have - // permission to retrieve VM number and would not generate - // an EVENT_VOICEMAIL_NUMBER. But if it happens, don't crash. - } + mCallerInfo = new CallerInfo().markAsVoiceMail(); } else { mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor); // Use the number entered by the user for display. @@ -258,7 +244,7 @@ public class CallerInfoAsyncQuery { //notify the listener that the query is complete. if (cw.listener != null) { if (DBG) log("notifying listener: " + cw.listener.getClass().toString() + - " for token: " + token); + " for token: " + token + mCallerInfo); cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo); } } @@ -315,23 +301,10 @@ public class CallerInfoAsyncQuery { // check to see if these are recognized numbers, and use shortcuts if we can. if (PhoneNumberUtils.isEmergencyNumber(number)) { cw.event = EVENT_EMERGENCY_NUMBER; + } else if (PhoneNumberUtils.isVoiceMailNumber(number)) { + cw.event = EVENT_VOICEMAIL_NUMBER; } else { - String vmNumber = null; - if (!sSkipVmCheck){ - try { - vmNumber = TelephonyManager.getDefault().getVoiceMailNumber(); - } catch (SecurityException ex) { - // Don't crash if this process doesn't have permission to - // retrieve VM number. It's still allowed to look up caller info. - // But don't try it again. - sSkipVmCheck = true; - } - } - if (PhoneNumberUtils.compare(number, vmNumber)) { - cw.event = EVENT_VOICEMAIL_NUMBER; - } else { - cw.event = EVENT_NEW_QUERY; - } + cw.event = EVENT_NEW_QUERY; } c.mHandler.startQuery (token, cw, contactRef, null, null, null, null); diff --git a/telephony/tests/TelephonyTest/AndroidManifest.xml b/telephony/tests/TelephonyTest/AndroidManifest.xml index c0cc0d5..b2a481b 100644 --- a/telephony/tests/TelephonyTest/AndroidManifest.xml +++ b/telephony/tests/TelephonyTest/AndroidManifest.xml @@ -28,8 +28,9 @@ </intent-filter> </activity> </application> - <instrumentation android:name=".TelephonyUnitTestRunner" - android:targetPackage="com.android.telephonytest" - android:label="Telephony unit tests InstrumentationRunner"> - </instrumentation> + <instrumentation android:name=".TelephonyUnitTestRunner" + android:targetPackage="com.android.telephonytest" + android:label="Telephony unit tests InstrumentationRunner"> + </instrumentation> + <uses-permission android:name="android.permission.READ_PHONE_STATE" /> </manifest> diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java index 5da940d..9e1af31 100644 --- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java +++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/TelephonyUnitTestRunner.java @@ -37,6 +37,7 @@ public class TelephonyUnitTestRunner extends InstrumentationTestRunner { public TestSuite getAllTests() { TestSuite suite = new InstrumentationTestSuite(this); suite.addTestSuite(com.android.telephonytest.unit.CallerInfoUnitTest.class); + suite.addTestSuite(com.android.telephonytest.unit.PhoneNumberUtilsUnitTest.class); return suite; } diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java index 4cd0266..0f24f15 100644 --- a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java +++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/CallerInfoUnitTest.java @@ -107,6 +107,16 @@ public class CallerInfoUnitTest extends AndroidTestCase { assertIsValidEmergencyCallerInfo(); } + // TODO: Add more tests: + /** + * Check if the voice mail number cannot be retrieved that the + * original phone number is preserved. + */ + /** + * Check the markAs* methods work. + */ + + // // Helpers // diff --git a/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java new file mode 100644 index 0000000..2d3c548 --- /dev/null +++ b/telephony/tests/TelephonyTest/src/com/android/telephonytest/unit/PhoneNumberUtilsUnitTest.java @@ -0,0 +1,63 @@ +/* + * 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 + * + * 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.telephonytest.unit; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; + +/* + * Check the PhoneNumberUtils utility class works as expected. + * + */ + +public class PhoneNumberUtilsUnitTest extends AndroidTestCase { + private String mVoiceMailNumber; + private static final String TAG = "PhoneNumberUtilsUnitTest"; + + @Override + protected void setUp() throws Exception { + super.setUp(); + // FIXME: Why are we getting a security exception here? The + // permission is declared in the manifest.... + // mVoiceMailNumber = TelephonyManager.getDefault().getVoiceMailNumber(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Basic checks for the VoiceMail number. + * Assumes READ_PHONE_STATE permission and we don't have it. + */ + // TODO: Figure out why we don't have the permission declared in the manifest. + @SmallTest + public void testWithNumberNotEqualToVoiceMail() throws Exception { + assertFalse(PhoneNumberUtils.isVoiceMailNumber("911")); + assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911")); + assertFalse(PhoneNumberUtils.isVoiceMailNumber("+18001234567")); + assertFalse(PhoneNumberUtils.isVoiceMailNumber("")); + assertFalse(PhoneNumberUtils.isVoiceMailNumber(null)); + // FIXME: + // assertTrue(PhoneNumberUtils.isVoiceMailNumber(mVoiceMailNumber)); + } + +} diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/HandlerThreadTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/HandlerThreadTest.java index c62f94f..f2025c6 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/os/HandlerThreadTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/os/HandlerThreadTest.java @@ -36,8 +36,11 @@ public class HandlerThreadTest extends TestCase { public void testHandlerThread() throws Exception { HandlerThread th1 = new HandlerThread("HandlerThreadTest") { protected void onLooperPrepared() { - mDidSetup = true; - mLooperTid = Process.myTid(); + synchronized (HandlerThreadTest.this) { + mDidSetup = true; + mLooperTid = Process.myTid(); + HandlerThreadTest.this.notify(); + } } }; @@ -49,14 +52,23 @@ public class HandlerThreadTest extends TestCase { assertTrue(th1.isAlive()); assertNotNull(th1.getLooper()); - /* - * Since getLooper() will block until the HandlerThread is setup, we are guaranteed - * that mDidSetup and mLooperTid will have been initalized. If they have not, then - * this test should fail - */ + // The call to getLooper() internally blocks until the looper is + // available, but will call onLooperPrepared() after that. So we + // need to block here to wait for our onLooperPrepared() to complete + // and fill in the values we expect. + synchronized (this) { + while (!mDidSetup) { + try { + wait(); + } catch (InterruptedException e) { + } + } + } + + // Make sure that the process was set. + assertNotSame(-1, mLooperTid); // Make sure that the onLooperPrepared() was called on a different thread. assertNotSame(Process.myTid(), mLooperTid); - assertTrue(mDidSetup); final Handler h1 = new Handler(th1.getLooper()) { public void handleMessage(Message msg) { diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index 8fea967..395e572 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -96,7 +96,9 @@ public class FileFilter { // Android layout tests are stored in "layout_tests". The following two // tests expect "LayoutTests" in their output. "storage/domstorage/localstorage/iframe-events.html", - "storage/domstorage/sessionstorage/iframe-events.html" + "storage/domstorage/sessionstorage/iframe-events.html", + // below tests (failed or crashes) are filtered out temporarily due to prioritizing + "editing/selection/move-left-right.html", }; static void fillIgnoreResultSet() { diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java index cc2f1f5..85e0422 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FsUtils.java @@ -1,5 +1,23 @@ +/* + * 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 + * + * 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.dumprendertree; +import com.android.dumprendertree.forwarder.ForwardService; + import android.util.Log; import java.io.BufferedOutputStream; @@ -12,6 +30,12 @@ import java.io.IOException; public class FsUtils { private static final String LOGTAG = "FsUtils"; + static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/"; + static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/"; + static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/"; + static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/"; + static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/"; + private FsUtils() { //no creation of instances } @@ -77,4 +101,24 @@ public class FsUtils { return status; } + public static String getTestUrl(String path) { + String url = null; + if (!path.startsWith(HTTP_TESTS_PREFIX)) { + url = "file://" + path; + } else { + ForwardService.getForwardService().startForwardService(); + if (path.startsWith(HTTPS_TESTS_PREFIX)) { + // still cut the URL after "http/tests/" + url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length()); + } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX) + && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX) + && !path.startsWith(HTTP_WML_TESTS_PREFIX)) { + url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length()); + } else { + url = "file://" + path; + } + } + return url; + } + } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index e4c8716..235e10e 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -19,6 +19,7 @@ package com.android.dumprendertree; import com.android.dumprendertree.TestShellActivity.DumpDataType; import com.android.dumprendertree.forwarder.AdbUtils; import com.android.dumprendertree.forwarder.ForwardServer; +import com.android.dumprendertree.forwarder.ForwardService; import android.app.Instrumentation; import android.content.Intent; @@ -143,17 +144,6 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh static final String LAYOUT_RESULTS_CRASHED_RESULT_FILE = "results/layout_tests_crashed.txt"; static final String LAYOUT_TESTS_RUNNER = "run_layout_tests.py"; - static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/"; - static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/"; - static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/"; - static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/"; - static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/"; - - - static final String DEFAULT_TEST_HOST = "android-browser-test.mtv.corp.google.com"; - static final String FORWARD_HOST_CONF = "/sdcard/drt_forward_host.txt"; - private ForwardServer fs8000, fs8080, fs8443; - private MyTestRecorder mResultRecorder; private Vector<String> mTestList; private boolean mRebaselineResults; @@ -162,45 +152,6 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh public LayoutTestsAutoTest() { super("com.android.dumprendertree", TestShellActivity.class); - - int addr = getForwardHostAddr(); - if(addr != -1) { - fs8000 = new ForwardServer(8000, addr, 8000); - fs8080 = new ForwardServer(8080, addr, 8080); - fs8443 = new ForwardServer(8443, addr, 8443); - } - } - - private int getForwardHostAddr() { - int addr = -1; - String host = null; - File forwardHostConf = new File(FORWARD_HOST_CONF); - if (forwardHostConf.isFile()) { - BufferedReader hostReader = null; - try { - hostReader = new BufferedReader(new FileReader(forwardHostConf)); - host = hostReader.readLine(); - Log.v(LOGTAG, "read forward host from file: " + host); - } catch (IOException ioe) { - Log.v(LOGTAG, "cannot read forward host from file", ioe); - } finally { - if (hostReader != null) { - try { - hostReader.close(); - } catch (IOException ioe) { - // burn!!! - } - } - } - } - if (host == null || host.length() == 0) - host = DEFAULT_TEST_HOST; - try { - addr = AdbUtils.resolve(host); - } catch (IOException ioe) { - Log.e(LOGTAG, "failed to resolve server address", ioe); - } - return addr; } // This function writes the result of the layout test to @@ -366,7 +317,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh Intent intent = new Intent(Intent.ACTION_VIEW); intent.setClass(activity, TestShellActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(TestShellActivity.TEST_URL, getTestUrl(test)); + intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(test)); intent.putExtra(TestShellActivity.RESULT_FILE, resultFile); intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout); activity.startActivity(intent); @@ -450,49 +401,10 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE"); - if(fs8000 != null) - fs8000.stop(); - if(fs8080 != null) - fs8080.stop(); - if(fs8443 != null) - fs8443.stop(); - + ForwardService.getForwardService().stopForwardService(); activity.finish(); } - private void startForwardServerIfNeeded() { - try { - if(fs8000 != null) - fs8000.start(); - if(fs8080 != null) - fs8080.start(); - if(fs8443 != null) - fs8443.start(); - } catch (IOException ioe) { - Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe); - } - } - - private String getTestUrl(String path) { - String url = null; - if (!path.startsWith(HTTP_TESTS_PREFIX)) { - url = "file://" + path; - } else { - startForwardServerIfNeeded(); - if (path.startsWith(HTTPS_TESTS_PREFIX)) { - // still cut the URL after "http/tests/" - url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length()); - } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX) - && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX) - && !path.startsWith(HTTP_WML_TESTS_PREFIX)) { - url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length()); - } else { - url = "file://" + path; - } - } - return url; - } - private String getTestPath() { LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation(); diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java index 71d9758..50b7c3f 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java @@ -1,3 +1,19 @@ +/* + * 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 + * + * 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.dumprendertree; import android.app.Activity; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java index 995c129..fbce78a 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java @@ -1,3 +1,19 @@ +/* + * 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 + * + * 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.dumprendertree; import android.app.Activity; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 663df83..3fef61c 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -16,6 +16,8 @@ package com.android.dumprendertree; +import com.android.dumprendertree.forwarder.ForwardService; + import android.app.Activity; import android.app.AlertDialog; import android.content.Context; @@ -184,6 +186,7 @@ public class TestShellActivity extends Activity implements LayoutTestController } catch (IOException ioe) { Log.w(LOGTAG, "Failed to close test list file.", ioe); } + ForwardService.getForwardService().stopForwardService(); finished(); } @@ -215,10 +218,9 @@ public class TestShellActivity extends Activity implements LayoutTestController builder.create().show(); return; } - url = "file://" + url; Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(TestShellActivity.TEST_URL, url); + intent.putExtra(TestShellActivity.TEST_URL, FsUtils.getTestUrl(url)); intent.putExtra(TIMEOUT_IN_MILLIS, 10000); executeIntent(intent); } @@ -289,13 +291,20 @@ public class TestShellActivity extends Activity implements LayoutTestController } public void finished() { - if (mUiAutoTestPath != null) { - //don't really finish here - moveToNextTest(); - } else { - if (mCallback != null) { - mCallback.finished(); + if (mTestPageLoaded) { + if (mUiAutoTestPath != null) { + //don't really finish here + moveToNextTest(); + } else { + if (mCallback != null) { + mCallback.finished(); + } } + } else { + // The test is complete but the page has not completed loading. We + // can't continue to the next test until both the test is finished + // and the page has stopped loading. + mReadyForNextTest = true; } } @@ -443,12 +452,14 @@ public class TestShellActivity extends Activity implements LayoutTestController @Override public void onPageFinished(WebView view, String url) { Log.v(LOGTAG, "onPageFinished, url=" + url); + mTestPageLoaded = true; super.onPageFinished(view, url); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { Log.v(LOGTAG, "onPageStarted, url=" + url); + mTestPageLoaded = false; super.onPageStarted(view, url, favicon); } @@ -478,6 +489,17 @@ public class TestShellActivity extends Activity implements LayoutTestController @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress == 100) { + + if (mReadyForNextTest) { + // In this case, the test has completed (i.e. called + // layoutTestController.notifyDone) before the page finished loading. This + // usually happens if the test is not invoked by an onload handler, rather + // directly in a script tag. Now that the page has finished loading, it is + // safe for DRT to go to the next test. + finished(); + return; + } + if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) { String url = mWebView.getUrl(); Log.v(LOGTAG, "Finished: "+ url); @@ -653,6 +675,8 @@ public class TestShellActivity extends Activity implements LayoutTestController mDumpDatabaseCallbacks = false; mCanOpenWindows = false; mEventSender.resetMouse(); + mTestPageLoaded = false; + mReadyForNextTest = false; } private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) { @@ -709,6 +733,9 @@ public class TestShellActivity extends Activity implements LayoutTestController private StringBuffer mConsoleMessages; private boolean mCanOpenWindows; + private boolean mTestPageLoaded = false; + private boolean mReadyForNextTest = false; + static final String TIMEOUT_STR = "**Test timeout"; static final int MSG_TIMEOUT = 0; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java index 9a3e9c2..c2ecf3a 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java @@ -1,3 +1,19 @@ +/* + * 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 + * + * 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.dumprendertree.forwarder; import android.util.Log; @@ -44,7 +60,6 @@ public class AdbUtils { DataInputStream dis = new DataInputStream(localSocket.getInputStream()); OutputStream os = localSocket.getOutputStream(); int count_read = 0; - byte[] buf = new byte[128]; if (localSocket == null || dis == null || os == null) return -1; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java index 74e018e..14f8fbe 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java @@ -1,3 +1,19 @@ +/* + * 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 + * + * 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.dumprendertree.forwarder; import android.util.Log; diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java new file mode 100644 index 0000000..8b7de6e --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardService.java @@ -0,0 +1,115 @@ +/* + * 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 + * + * 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.dumprendertree.forwarder; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +import android.util.Log; + +public class ForwardService { + + private ForwardServer fs8000, fs8080, fs8443; + + private static ForwardService inst; + + private static final String LOGTAG = "ForwardService"; + + private static final String DEFAULT_TEST_HOST = "android-browser-test.mtv.corp.google.com"; + + private static final String FORWARD_HOST_CONF = "/sdcard/drt_forward_host.txt"; + + private ForwardService() { + int addr = getForwardHostAddr(); + if (addr != -1) { + fs8000 = new ForwardServer(8000, addr, 8000); + fs8080 = new ForwardServer(8080, addr, 8080); + fs8443 = new ForwardServer(8443, addr, 8443); + } + } + + public static ForwardService getForwardService() { + if (inst == null) { + inst = new ForwardService(); + } + return inst; + } + + public void startForwardService() { + try { + if (fs8000 != null) + fs8000.start(); + if (fs8080 != null) + fs8080.start(); + if (fs8443 != null) + fs8443.start(); + } catch (IOException ioe) { + Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe); + return; + } + } + + public void stopForwardService() { + if (fs8000 != null) { + fs8000.stop(); + fs8000 = null; + } + if (fs8080 != null) { + fs8080.stop(); + fs8080 = null; + } + if (fs8443 != null) { + fs8443.stop(); + fs8443 = null; + } + Log.v(LOGTAG, "forwarders stopped."); + } + + private static int getForwardHostAddr() { + int addr = -1; + String host = null; + File forwardHostConf = new File(FORWARD_HOST_CONF); + if (forwardHostConf.isFile()) { + BufferedReader hostReader = null; + try { + hostReader = new BufferedReader(new FileReader(forwardHostConf)); + host = hostReader.readLine(); + Log.v(LOGTAG, "read forward host from file: " + host); + } catch (IOException ioe) { + Log.v(LOGTAG, "cannot read forward host from file", ioe); + } finally { + if (hostReader != null) { + try { + hostReader.close(); + } catch (IOException ioe) { + // burn!!! + } + } + } + } + if (host == null || host.length() == 0) + host = DEFAULT_TEST_HOST; + try { + addr = AdbUtils.resolve(host); + } catch (IOException ioe) { + Log.e(LOGTAG, "failed to resolve server address", ioe); + } + return addr; + } +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java index e1e04a7..a1f3cdf 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java @@ -1,3 +1,19 @@ +/* + * 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 + * + * 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.dumprendertree.forwarder; import android.util.Log; diff --git a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java index dc959f5..aea124b 100644 --- a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java +++ b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java @@ -29,7 +29,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; -import java.util.Hashtable; import java.util.Set; import java.util.Map; import java.util.Map.Entry; @@ -145,7 +144,7 @@ public class ContentProviderOperationTest extends TestCase { public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // Return cursor over specific set of values - return getCursor(sTestValues1); + return getCursor(sTestValues1, 1); } }, null, 0); } catch (OperationApplicationException e) { @@ -153,11 +152,62 @@ public class ContentProviderOperationTest extends TestCase { } } + public void testAssertNoValues() { + // Build an operation to assert values match provider + ContentProviderOperation op1 = ContentProviderOperation.newAssertQuery(sTestUri1) + .withExpectedCount(1).build(); + + try { + // Assert that values match from cursor + ContentProviderResult result = op1.apply(new TestContentProvider() { + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + // Return cursor over specific set of values + return getCursor(sTestValues1, 1); + } + }, null, 0); + } catch (OperationApplicationException e) { + fail("newAssert() failed"); + } + + ContentProviderOperation op2 = ContentProviderOperation.newAssertQuery(sTestUri1) + .withExpectedCount(0).build(); + + try { + // Assert that values match from cursor + ContentProviderResult result = op2.apply(new TestContentProvider() { + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + // Return cursor over specific set of values + return getCursor(sTestValues1, 0); + } + }, null, 0); + } catch (OperationApplicationException e) { + fail("newAssert() failed"); + } + + ContentProviderOperation op3 = ContentProviderOperation.newAssertQuery(sTestUri1) + .withExpectedCount(2).build(); + + try { + // Assert that values match from cursor + ContentProviderResult result = op3.apply(new TestContentProvider() { + public Cursor query(Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + // Return cursor over specific set of values + return getCursor(sTestValues1, 5); + } + }, null, 0); + fail("we expect the exception to be thrown"); + } catch (OperationApplicationException e) { + } + } + /** * Build a {@link Cursor} with a single row that contains all values * provided through the given {@link ContentValues}. */ - private Cursor getCursor(ContentValues contentValues) { + private Cursor getCursor(ContentValues contentValues, int numRows) { final Set<Entry<String, Object>> valueSet = contentValues.valueSet(); final String[] keys = new String[valueSet.size()]; final Object[] values = new Object[valueSet.size()]; @@ -170,7 +220,9 @@ public class ContentProviderOperationTest extends TestCase { } final MatrixCursor cursor = new MatrixCursor(keys); - cursor.addRow(values); + for (i = 0; i < numRows; i++) { + cursor.addRow(values); + } return cursor; } diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 9a11404..e3d8bf4 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -1350,7 +1350,7 @@ public class WifiStateTracker extends NetworkStateTracker { */ private synchronized void requestPolledInfo(WifiInfo info, boolean polling) { - int newRssi = WifiNative.getRssiCommand(); + int newRssi = (polling ? WifiNative.getRssiApproxCommand() : WifiNative.getRssiCommand()); if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values /* some implementations avoid negative values by adding 256 * so we need to adjust for that here. |
