Merge change I272af007 into eclair-mr2
* changes:
a possible fix for the bug http://b/issue?id=2200637
diff --git a/Android.mk b/Android.mk
index a40d3f9..f538d8e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -209,6 +209,7 @@
frameworks/base/core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
frameworks/base/core/java/android/app/Notification.aidl \
frameworks/base/core/java/android/app/PendingIntent.aidl \
+ frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \
frameworks/base/core/java/android/content/ComponentName.aidl \
frameworks/base/core/java/android/content/Intent.aidl \
frameworks/base/core/java/android/content/IntentSender.aidl \
diff --git a/api/5.xml b/api/5.xml
index 6148047..73df0cb 100644
--- a/api/5.xml
+++ b/api/5.xml
@@ -162357,17 +162357,6 @@
visibility="protected"
>
</field>
-<field name="FLAG_USE_CHILD_DRAWING_ORDER"
- type="int"
- transient="false"
- volatile="false"
- value="1024"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
<field name="FOCUS_AFTER_DESCENDANTS"
type="int"
transient="false"
diff --git a/api/current.xml b/api/current.xml
index c84f13e..7df3192 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -162753,6 +162753,17 @@
visibility="public"
>
</method>
+<method name="isChildrenDrawingOrderEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
<method name="isChildrenDrawnWithCacheEnabled"
return="boolean"
abstract="false"
@@ -163136,6 +163147,19 @@
<parameter name="enabled" type="boolean">
</parameter>
</method>
+<method name="setChildrenDrawingOrderEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
<method name="setChildrenDrawnWithCacheEnabled"
return="void"
abstract="false"
@@ -163303,17 +163327,6 @@
visibility="protected"
>
</field>
-<field name="FLAG_USE_CHILD_DRAWING_ORDER"
- type="int"
- transient="false"
- volatile="false"
- value="1024"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="protected"
->
-</field>
<field name="FOCUS_AFTER_DESCENDANTS"
type="int"
transient="false"
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
index 594c0e8..34f30a7 100755
--- a/core/java/android/os/IHardwareService.aidl
+++ b/core/java/android/os/IHardwareService.aidl
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2007, The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * 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
+ * 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
+ * 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.
*/
@@ -23,13 +23,13 @@
void vibrate(long milliseconds, IBinder token);
void vibratePattern(in long[] pattern, int repeat, IBinder token);
void cancelVibrate(IBinder token);
-
+
// flashlight support
boolean getFlashlightEnabled();
void setFlashlightEnabled(boolean on);
void enableCameraFlash(int milliseconds);
// for the phone
- void setAttentionLight(boolean on);
+ void setAttentionLight(boolean on, int color);
}
diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java
index cab9dc1..f01659e8 100644
--- a/core/java/android/pim/vcard/ContactStruct.java
+++ b/core/java/android/pim/vcard/ContactStruct.java
@@ -471,20 +471,24 @@
}
StringBuilder builder = new StringBuilder();
String trimed = data.trim();
- int length = trimed.length();
- for (int i = 0; i < length; i++) {
- char ch = trimed.charAt(i);
- if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
- builder.append(ch);
+ final String formattedNumber;
+ if (type == Phone.TYPE_PAGER) {
+ formattedNumber = trimed;
+ } else {
+ final int length = trimed.length();
+ for (int i = 0; i < length; i++) {
+ char ch = trimed.charAt(i);
+ if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
+ builder.append(ch);
+ }
}
- }
- // Use NANP in default when there's no information about locale.
- final int formattingType = (VCardConfig.isJapaneseDevice(mVCardType) ?
- PhoneNumberUtils.FORMAT_JAPAN : PhoneNumberUtils.FORMAT_NANP);
- final String formattedPhoneNumber =
- PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
- PhoneData phoneData = new PhoneData(type, formattedPhoneNumber, label, isPrimary);
+ // Use NANP in default when there's no information about locale.
+ final int formattingType = (VCardConfig.isJapaneseDevice(mVCardType) ?
+ PhoneNumberUtils.FORMAT_JAPAN : PhoneNumberUtils.FORMAT_NANP);
+ formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType);
+ }
+ PhoneData phoneData = new PhoneData(type, formattedNumber, label, isPrimary);
mPhoneList.add(phoneData);
}
@@ -726,8 +730,8 @@
// which is correct behavior from the view of vCard 2.1.
// But we want it to be separated, so do the separation here.
final List<String> phoneticNameList =
- VCardUtils.constructListFromValue(propValue,
- VCardConfig.isV30(mVCardType));
+ VCardUtils.constructListFromValue(propValue,
+ VCardConfig.isV30(mVCardType));
handlePhoneticNameFromSound(phoneticNameList);
} else {
// Ignore this field since Android cannot understand what it is.
@@ -856,7 +860,8 @@
}
} else if (propName.equals(Constants.PROPERTY_TEL)) {
final Collection<String> typeCollection = paramMap.get(Constants.PARAM_TYPE);
- final Object typeObject = VCardUtils.getPhoneTypeFromStrings(typeCollection);
+ final Object typeObject =
+ VCardUtils.getPhoneTypeFromStrings(typeCollection, propValue);
final int type;
final String label;
if (typeObject instanceof Integer) {
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
index b8f4cfd..9d72c5f 100644
--- a/core/java/android/pim/vcard/VCardComposer.java
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -53,6 +53,7 @@
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
+import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -144,6 +145,7 @@
private static final String VCARD_PARAM_ENCODING_BASE64_V30 = "ENCODING=b";
private static final String SHIFT_JIS = "SHIFT_JIS";
+ private static final String UTF_8 = "UTF-8";
/**
* Special URI for testing.
@@ -359,12 +361,9 @@
mIsV30 = VCardConfig.isV30(vcardType);
mUsesQuotedPrintable = VCardConfig.usesQuotedPrintable(vcardType);
mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
- mIsJapaneseMobilePhone = VCardConfig
- .needsToConvertPhoneticString(vcardType);
- mOnlyOneNoteFieldIsAvailable = VCardConfig
- .onlyOneNoteFieldIsAvailable(vcardType);
- mUsesAndroidProperty = VCardConfig
- .usesAndroidSpecificProperty(vcardType);
+ mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType);
+ mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType);
+ mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType);
mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
mUsesUtf8 = VCardConfig.usesUtf8(vcardType);
mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
@@ -374,17 +373,31 @@
mHandlerList = new ArrayList<OneEntryHandler>();
if (mIsDoCoMo) {
- mCharsetString = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
+ String charset;
+ try {
+ charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
+ } catch (UnsupportedCharsetException e) {
+ Log.e(LOG_TAG, "DoCoMo-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
+ charset = SHIFT_JIS;
+ }
+ mCharsetString = charset;
// Do not use mCharsetString bellow since it is different from "SHIFT_JIS" but
// may be "DOCOMO_SHIFT_JIS" or something like that (internal expression used in
// Android, not shown to the public).
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
} else if (mUsesShiftJis) {
- mCharsetString = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
+ String charset;
+ try {
+ charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
+ } catch (UnsupportedCharsetException e) {
+ Log.e(LOG_TAG, "Vendor-specific SHIFT_JIS was not found. Use SHIFT_JIS as is.");
+ charset = SHIFT_JIS;
+ }
+ mCharsetString = charset;
mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS;
} else {
- mCharsetString = "UTF-8";
- mVCardCharsetParameter = "CHARSET=UTF-8";
+ mCharsetString = UTF_8;
+ mVCardCharsetParameter = "CHARSET=" + UTF_8;
}
}
@@ -702,34 +715,46 @@
}
}
- final String familyName = primaryContentValues
- .getAsString(StructuredName.FAMILY_NAME);
- final String middleName = primaryContentValues
- .getAsString(StructuredName.MIDDLE_NAME);
- final String givenName = primaryContentValues
- .getAsString(StructuredName.GIVEN_NAME);
- final String prefix = primaryContentValues
- .getAsString(StructuredName.PREFIX);
- final String suffix = primaryContentValues
- .getAsString(StructuredName.SUFFIX);
- final String displayName = primaryContentValues
- .getAsString(StructuredName.DISPLAY_NAME);
+ final String familyName = primaryContentValues.getAsString(StructuredName.FAMILY_NAME);
+ final String middleName = primaryContentValues.getAsString(StructuredName.MIDDLE_NAME);
+ final String givenName = primaryContentValues.getAsString(StructuredName.GIVEN_NAME);
+ final String prefix = primaryContentValues.getAsString(StructuredName.PREFIX);
+ final String suffix = primaryContentValues.getAsString(StructuredName.SUFFIX);
+ final String displayName = primaryContentValues.getAsString(StructuredName.DISPLAY_NAME);
if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) {
+ final boolean shouldAppendCharsetParameterToName =
+ !(mIsV30 && UTF_8.equalsIgnoreCase(mCharsetString)) &&
+ shouldAppendCharsetParameters(Arrays.asList(
+ familyName, givenName, middleName, prefix, suffix));
+ final boolean reallyUseQuotedPrintableToName =
+ (!mRefrainsQPToPrimaryProperties &&
+ !(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) &&
+ VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) &&
+ VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) &&
+ VCardUtils.containsOnlyNonCrLfPrintableAscii(prefix) &&
+ VCardUtils.containsOnlyNonCrLfPrintableAscii(suffix)));
+
+ final String formattedName;
+ if (!TextUtils.isEmpty(displayName)) {
+ formattedName = displayName;
+ } else {
+ formattedName = VCardUtils.constructNameFromElements(
+ VCardConfig.getNameOrderType(mVCardType),
+ familyName, middleName, givenName, prefix, suffix);
+ }
+ final boolean shouldAppendCharsetParameterToFN =
+ !(mIsV30 && UTF_8.equalsIgnoreCase(mCharsetString)) &&
+ shouldAppendCharsetParameter(formattedName);
+ final boolean reallyUseQuotedPrintableToFN =
+ !mRefrainsQPToPrimaryProperties &&
+ !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName);
+
final String encodedFamily;
final String encodedGiven;
final String encodedMiddle;
final String encodedPrefix;
final String encodedSuffix;
-
- final boolean reallyUseQuotedPrintableToName =
- (!mRefrainsQPToPrimaryProperties &&
- !(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(prefix) &&
- VCardUtils.containsOnlyNonCrLfPrintableAscii(suffix)));
-
if (reallyUseQuotedPrintableToName) {
encodedFamily = encodeQuotedPrintable(familyName);
encodedGiven = encodeQuotedPrintable(givenName);
@@ -744,59 +769,61 @@
encodedSuffix = escapeCharacters(suffix);
}
+ final String encodedFormattedname =
+ (reallyUseQuotedPrintableToFN ?
+ encodeQuotedPrintable(formattedName) : escapeCharacters(formattedName));
+
builder.append(Constants.PROPERTY_N);
- if (shouldAppendCharsetParameters(Arrays.asList(
- encodedFamily, encodedGiven, encodedMiddle, encodedPrefix, encodedSuffix))) {
- builder.append(VCARD_PARAM_SEPARATOR);
- builder.append(mVCardCharsetParameter);
- }
- if (reallyUseQuotedPrintableToName) {
- builder.append(VCARD_PARAM_SEPARATOR);
- builder.append(VCARD_PARAM_ENCODING_QP);
- }
-
- builder.append(VCARD_DATA_SEPARATOR);
- builder.append(encodedFamily);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(encodedGiven);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(encodedMiddle);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(encodedPrefix);
- builder.append(VCARD_ITEM_SEPARATOR);
- builder.append(encodedSuffix);
- builder.append(VCARD_END_OF_LINE);
-
- final String formattedName;
- if (!TextUtils.isEmpty(displayName)) {
- formattedName = displayName;
+ if (mIsDoCoMo) {
+ if (shouldAppendCharsetParameterToName) {
+ builder.append(VCARD_PARAM_SEPARATOR);
+ builder.append(mVCardCharsetParameter);
+ }
+ if (reallyUseQuotedPrintableToName) {
+ builder.append(VCARD_PARAM_SEPARATOR);
+ builder.append(VCARD_PARAM_ENCODING_QP);
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ // DoCoMo phones require that all the elements in the "family name" field.
+ builder.append(formattedName);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
} else {
- formattedName = VCardUtils.constructNameFromElements(
- VCardConfig.getNameOrderType(mVCardType),
- encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
+ if (shouldAppendCharsetParameterToName) {
+ builder.append(VCARD_PARAM_SEPARATOR);
+ builder.append(mVCardCharsetParameter);
+ }
+ if (reallyUseQuotedPrintableToName) {
+ builder.append(VCARD_PARAM_SEPARATOR);
+ builder.append(VCARD_PARAM_ENCODING_QP);
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedFamily);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedGiven);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedMiddle);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedPrefix);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedSuffix);
}
-
- final boolean reallyUseQuotedPrintableToFullname =
- !mRefrainsQPToPrimaryProperties &&
- !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName);
-
- final String encodedFullname =
- reallyUseQuotedPrintableToFullname ?
- encodeQuotedPrintable(formattedName) :
- escapeCharacters(formattedName);
+ builder.append(VCARD_END_OF_LINE);
// FN property
builder.append(Constants.PROPERTY_FN);
- if (shouldAppendCharsetParameter(encodedFullname)) {
+ if (shouldAppendCharsetParameterToFN) {
builder.append(VCARD_PARAM_SEPARATOR);
builder.append(mVCardCharsetParameter);
}
- if (reallyUseQuotedPrintableToFullname) {
+ if (reallyUseQuotedPrintableToFN) {
builder.append(VCARD_PARAM_SEPARATOR);
builder.append(VCARD_PARAM_ENCODING_QP);
}
builder.append(VCARD_DATA_SEPARATOR);
- builder.append(encodedFullname);
+ builder.append(encodedFormattedname);
builder.append(VCARD_END_OF_LINE);
} else if (!TextUtils.isEmpty(displayName)) {
final boolean reallyUseQuotedPrintableToDisplayName =
@@ -808,7 +835,7 @@
escapeCharacters(displayName);
builder.append(Constants.PROPERTY_N);
- if (shouldAppendCharsetParameter(encodedDisplayName)) {
+ if (shouldAppendCharsetParameter(displayName)) {
builder.append(VCARD_PARAM_SEPARATOR);
builder.append(mVCardCharsetParameter);
}
@@ -826,7 +853,7 @@
if (mIsV30) {
builder.append(Constants.PROPERTY_FN);
// TODO: Not allowed formally...
- if (shouldAppendCharsetParameter(encodedDisplayName)) {
+ if (shouldAppendCharsetParameter(displayName)) {
builder.append(VCARD_PARAM_SEPARATOR);
builder.append(mVCardCharsetParameter);
}
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
index c59e258..05e6730 100644
--- a/core/java/android/pim/vcard/VCardUtils.java
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -98,7 +98,11 @@
* Returns Interger when the given types can be parsed as known type. Returns String object
* when not, which should be set to label.
*/
- public static Object getPhoneTypeFromStrings(Collection<String> types) {
+ public static Object getPhoneTypeFromStrings(Collection<String> types,
+ String number) {
+ if (number == null) {
+ number = "";
+ }
int type = -1;
String label = null;
boolean isFax = false;
@@ -117,7 +121,20 @@
}
Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString);
if (tmp != null) {
- type = tmp;
+ final int typeCandidate = tmp;
+ // TYPE_PAGER is prefered when the number contains @ surronded by
+ // a pager number and a domain name.
+ // e.g.
+ // o 1111@domain.com
+ // x @domain.com
+ // x 1111@
+ final int indexOfAt = number.indexOf("@");
+ if ((typeCandidate == Phone.TYPE_PAGER
+ && 0 < indexOfAt && indexOfAt < number.length() - 1)
+ || type < 0
+ || type == Phone.TYPE_CUSTOM) {
+ type = tmp;
+ }
} else if (type < 0) {
type = Phone.TYPE_CUSTOM;
label = typeString;
@@ -345,7 +362,7 @@
if (TextUtils.isEmpty(str)) {
return true;
}
-
+
final int length = str.length();
final int asciiFirst = 0x20;
final int asciiLast = 0x126;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f7b7f02..e2f15c7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -150,6 +150,8 @@
/**
* When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
* to get the index of the child to draw for that iteration.
+ *
+ * @hide
*/
protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
@@ -1307,11 +1309,14 @@
* if you want to change the drawing order of children. By default, it
* returns i.
* <p>
- * NOTE: In order for this method to be called, the
- * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set.
+ * NOTE: In order for this method to be called, you must enable child ordering
+ * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
*
* @param i The current iteration.
* @return The index of the child to draw this iteration.
+ *
+ * @see #setChildrenDrawingOrderEnabled(boolean)
+ * @see #isChildrenDrawingOrderEnabled()
*/
protected int getChildDrawingOrder(int childCount, int i) {
return i;
@@ -2706,6 +2711,35 @@
setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
}
+ /**
+ * Indicates whether the ViewGroup is drawing its children in the order defined by
+ * {@link #getChildDrawingOrder(int, int)}.
+ *
+ * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
+ * false otherwise
+ *
+ * @see #setChildrenDrawingOrderEnabled(boolean)
+ * @see #getChildDrawingOrder(int, int)
+ */
+ @ViewDebug.ExportedProperty
+ protected boolean isChildrenDrawingOrderEnabled() {
+ return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
+ }
+
+ /**
+ * Tells the ViewGroup whether to draw its children in the order defined by the method
+ * {@link #getChildDrawingOrder(int, int)}.
+ *
+ * @param enabled true if the order of the children when drawing is determined by
+ * {@link #getChildDrawingOrder(int, int)}, false otherwise
+ *
+ * @see #isChildrenDrawingOrderEnabled()
+ * @see #getChildDrawingOrder(int, int)
+ */
+ protected void setChildrenDrawingOrderEnabled(boolean enabled) {
+ setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
+ }
+
private void setBooleanFlag(int flag, boolean value) {
if (value) {
mGroupFlags |= flag;
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index 07bb1ab..50aa967 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -79,7 +79,7 @@
static void android_media_ToneGenerator_native_setup(JNIEnv *env, jobject thiz,
jint streamType, jint volume) {
- ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume));
+ ToneGenerator *lpToneGen = new ToneGenerator(streamType, AudioSystem::linearToLog(volume), true);
env->SetIntField(thiz, fields.context, 0);
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 503cb31..008468c 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -314,6 +314,11 @@
};
bool processAudioBuffer(const sp<ClientRecordThread>& thread);
+ status_t openRecord(uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags);
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;
@@ -341,6 +346,7 @@
uint32_t mNewPosition;
uint32_t mUpdatePeriod;
audio_io_handle_t mInput;
+ uint32_t mFlags;
};
}; // namespace android
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 981c2f6..14b30ae 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -391,6 +391,14 @@
};
bool processAudioBuffer(const sp<AudioTrackThread>& thread);
+ status_t createTrack(int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer,
+ audio_io_handle_t output);
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index c884c2c..1ad1f26 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -151,7 +151,7 @@
NUM_SUP_TONES = LAST_SUP_TONE-FIRST_SUP_TONE+1
};
- ToneGenerator(int streamType, float volume);
+ ToneGenerator(int streamType, float volume, bool threadCanCallJava = false);
~ToneGenerator();
bool startTone(int toneType, int durationMs = -1);
@@ -242,6 +242,7 @@
static const ToneDescriptor sToneDescriptors[];
+ bool mThreadCanCallJava;
unsigned int mTotalSmp; // Total number of audio samples played (gives current time)
unsigned int mNextSegSmp; // Position of next segment transition expressed in samples
// NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly
diff --git a/media/java/android/media/ThumbnailUtil.java b/media/java/android/media/ThumbnailUtil.java
index 7c6bca3..a33adc9 100644
--- a/media/java/android/media/ThumbnailUtil.java
+++ b/media/java/android/media/ThumbnailUtil.java
@@ -35,9 +35,7 @@
import android.media.MediaMetadataRetriever;
import android.media.MediaFile.MediaFileType;
-import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
@@ -296,7 +294,7 @@
* @param uri URI of original image
* @param origId image id
* @param kind either MINI_KIND or MICRO_KIND
- * @param saveImage Whether to save MINI_KIND thumbnail obtained in this method.
+ * @param saveMini Whether to save MINI_KIND thumbnail obtained in this method.
* @return Bitmap
*/
public static Bitmap createImageThumbnail(ContentResolver cr, String filePath, Uri uri,
@@ -306,20 +304,12 @@
ThumbnailUtil.THUMBNAIL_TARGET_SIZE : ThumbnailUtil.MINI_THUMB_TARGET_SIZE;
int maxPixels = wantMini ?
ThumbnailUtil.THUMBNAIL_MAX_NUM_PIXELS : ThumbnailUtil.MINI_THUMB_MAX_NUM_PIXELS;
- byte[] thumbData = null;
+ SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();
Bitmap bitmap = null;
MediaFileType fileType = MediaFile.getFileType(filePath);
if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {
- thumbData = createThumbnailFromEXIF(filePath, targetSize);
- }
-
- if (thumbData != null) {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize = computeSampleSize(options, targetSize, maxPixels);
- options.inDither = false;
- options.inPreferredConfig = Bitmap.Config.ARGB_8888;
- options.inJustDecodeBounds = false;
- bitmap = BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, options);
+ createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);
+ bitmap = sizedThumbnailBitmap.mBitmap;
}
if (bitmap == null) {
@@ -331,9 +321,11 @@
}
if (saveMini) {
- if (thumbData != null) {
- ThumbnailUtil.storeThumbnail(cr, origId, thumbData, bitmap.getWidth(),
- bitmap.getHeight());
+ if (sizedThumbnailBitmap.mThumbnailData != null) {
+ ThumbnailUtil.storeThumbnail(cr, origId,
+ sizedThumbnailBitmap.mThumbnailData,
+ sizedThumbnailBitmap.mThumbnailWidth,
+ sizedThumbnailBitmap.mThumbnailHeight);
} else {
ThumbnailUtil.storeThumbnail(cr, origId, bitmap);
}
@@ -521,31 +513,69 @@
}
}
- // Extract thumbnail in image that meets the targetSize criteria.
- static byte[] createThumbnailFromEXIF(String filePath, int targetSize) {
- if (filePath == null) return null;
+ // SizedThumbnailBitmap contains the bitmap, which is downsampled either from
+ // the thumbnail in exif or the full image.
+ // mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail is not null.
+ // The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
+ private static class SizedThumbnailBitmap {
+ public byte[] mThumbnailData;
+ public Bitmap mBitmap;
+ public int mThumbnailWidth;
+ public int mThumbnailHeight;
+ }
+ // Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
+ // The functions returns a SizedThumbnailBitmap,
+ // which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
+ private static void createThumbnailFromEXIF(String filePath, int targetSize,
+ int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {
+ if (filePath == null) return;
+
+ ExifInterface exif = null;
+ byte [] thumbData = null;
try {
- ExifInterface exif = new ExifInterface(filePath);
- if (exif == null) return null;
- byte [] thumbData = exif.getThumbnail();
- if (thumbData == null) return null;
- // Sniff the size of the EXIF thumbnail before decoding it. Photos
- // from the device will pass, but images that are side loaded from
- // other cameras may not.
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, options);
-
- int width = options.outWidth;
- int height = options.outHeight;
-
- if (width >= targetSize && height >= targetSize) {
- return thumbData;
+ exif = new ExifInterface(filePath);
+ if (exif != null) {
+ thumbData = exif.getThumbnail();
}
} catch (IOException ex) {
Log.w(TAG, ex);
}
- return null;
+
+ BitmapFactory.Options fullOptions = new BitmapFactory.Options();
+ BitmapFactory.Options exifOptions = new BitmapFactory.Options();
+ int exifThumbWidth = 0;
+ int fullThumbWidth = 0;
+
+ // Compute exifThumbWidth.
+ if (thumbData != null) {
+ exifOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);
+ exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);
+ exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;
+ }
+
+ // Compute fullThumbWidth.
+ fullOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(filePath, fullOptions);
+ fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);
+ fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;
+
+ // Choose the larger thumbnail as the returning sizedThumbBitmap.
+ if (exifThumbWidth > fullThumbWidth) {
+ int width = exifOptions.outWidth;
+ int height = exifOptions.outHeight;
+ exifOptions.inJustDecodeBounds = false;
+ sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,
+ thumbData.length, exifOptions);
+ if (sizedThumbBitmap.mBitmap != null) {
+ sizedThumbBitmap.mThumbnailData = thumbData;
+ sizedThumbBitmap.mThumbnailWidth = width;
+ sizedThumbBitmap.mThumbnailHeight = height;
+ }
+ } else {
+ fullOptions.inJustDecodeBounds = false;
+ sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
+ }
}
}
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 5e35564..e63c0d2 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -101,11 +101,6 @@
return INVALID_OPERATION;
}
- const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
- if (audioFlinger == 0) {
- return NO_INIT;
- }
-
if (inputSource == AUDIO_SOURCE_DEFAULT) {
inputSource = AUDIO_SOURCE_MIC;
}
@@ -171,22 +166,14 @@
notificationFrames = frameCount/2;
}
- // open record channel
- status_t status;
- sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), mInput,
- sampleRate, format,
- channelCount,
- frameCount,
- ((uint16_t)flags) << 16,
- &status);
- if (record == 0) {
- LOGE("AudioFlinger could not create record track, status: %d", status);
+ // create the IAudioRecord
+ status_t status = openRecord(sampleRate, format, channelCount,
+ frameCount, flags);
+
+ if (status != NO_ERROR) {
return status;
}
- sp<IMemory> cblk = record->getCblk();
- if (cblk == 0) {
- return NO_INIT;
- }
+
if (cbf != 0) {
mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava);
if (mClientRecordThread == 0) {
@@ -196,11 +183,6 @@
mStatus = NO_ERROR;
- mAudioRecord = record;
- mCblkMemory = cblk;
- mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
- mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- mCblk->out = 0;
mFormat = format;
// Update buffer size in case it has been limited by AudioFlinger during track creation
mFrameCount = mCblk->frameCount;
@@ -217,6 +199,7 @@
mNewPosition = 0;
mUpdatePeriod = 0;
mInputSource = (uint8_t)inputSource;
+ mFlags = flags;
return NO_ERROR;
}
@@ -284,15 +267,26 @@
if (android_atomic_or(1, &mActive) == 0) {
ret = AudioSystem::startInput(mInput);
if (ret == NO_ERROR) {
- mNewPosition = mCblk->user + mUpdatePeriod;
- mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
- mCblk->waitTimeMs = 0;
- if (t != 0) {
- t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
- } else {
- setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
- }
ret = mAudioRecord->start();
+ if (ret == DEAD_OBJECT) {
+ LOGV("start() dead IAudioRecord: creating a new one");
+ ret = openRecord(mCblk->sampleRate, mFormat, mChannelCount,
+ mFrameCount, mFlags);
+ }
+ if (ret == NO_ERROR) {
+ mNewPosition = mCblk->user + mUpdatePeriod;
+ mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ mCblk->waitTimeMs = 0;
+ if (t != 0) {
+ t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
+ } else {
+ setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
+ }
+ } else {
+ LOGV("start() failed");
+ AudioSystem::stopInput(mInput);
+ android_atomic_and(~1, &mActive);
+ }
}
}
@@ -396,10 +390,48 @@
// -------------------------------------------------------------------------
+status_t AudioRecord::openRecord(
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags)
+{
+ status_t status;
+ const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
+ if (audioFlinger == 0) {
+ return NO_INIT;
+ }
+
+ sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), mInput,
+ sampleRate, format,
+ channelCount,
+ frameCount,
+ ((uint16_t)flags) << 16,
+ &status);
+ if (record == 0) {
+ LOGE("AudioFlinger could not create record track, status: %d", status);
+ return status;
+ }
+ sp<IMemory> cblk = record->getCblk();
+ if (cblk == 0) {
+ LOGE("Could not get control block");
+ return NO_INIT;
+ }
+ mAudioRecord.clear();
+ mAudioRecord = record;
+ mCblkMemory.clear();
+ mCblkMemory = cblk;
+ mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
+ mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
+ mCblk->out = 0;
+
+ return NO_ERROR;
+}
+
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
int active;
- int timeout = 0;
status_t result;
audio_track_cblk_t* cblk = mCblk;
uint32_t framesReq = audioBuffer->frameCount;
@@ -411,25 +443,40 @@
uint32_t framesReady = cblk->framesReady();
if (framesReady == 0) {
- Mutex::Autolock _l(cblk->lock);
+ cblk->lock.lock();
goto start_loop_here;
while (framesReady == 0) {
active = mActive;
- if (UNLIKELY(!active))
+ if (UNLIKELY(!active)) {
+ cblk->lock.unlock();
return NO_MORE_BUFFERS;
- if (UNLIKELY(!waitCount))
+ }
+ if (UNLIKELY(!waitCount)) {
+ cblk->lock.unlock();
return WOULD_BLOCK;
- timeout = 0;
+ }
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
if (__builtin_expect(result!=NO_ERROR, false)) {
cblk->waitTimeMs += waitTimeMs;
if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
LOGW( "obtainBuffer timed out (is the CPU pegged?) "
"user=%08x, server=%08x", cblk->user, cblk->server);
- timeout = 1;
+ cblk->lock.unlock();
+ result = mAudioRecord->start();
+ if (result == DEAD_OBJECT) {
+ LOGW("obtainBuffer() dead IAudioRecord: creating a new one");
+ result = openRecord(cblk->sampleRate, mFormat, mChannelCount,
+ mFrameCount, mFlags);
+ if (result == NO_ERROR) {
+ cblk = mCblk;
+ cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ }
+ }
+ cblk->lock.lock();
cblk->waitTimeMs = 0;
}
if (--waitCount == 0) {
+ cblk->lock.unlock();
return TIMED_OUT;
}
}
@@ -437,13 +484,9 @@
start_loop_here:
framesReady = cblk->framesReady();
}
+ cblk->lock.unlock();
}
- LOGW_IF(timeout,
- "*** SERIOUS WARNING *** obtainBuffer() timed out "
- "but didn't need to be locked. We recovered, but "
- "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server);
-
cblk->waitTimeMs = 0;
if (framesReq > framesReady) {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 4b9d272..8529a8e 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -122,11 +122,6 @@
return INVALID_OPERATION;
}
- const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
- if (audioFlinger == 0) {
- LOGE("Could not get audioflinger");
- return NO_INIT;
- }
int afSampleRate;
if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
return NO_INIT;
@@ -217,28 +212,16 @@
}
}
- // create the track
- status_t status;
- sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),
- streamType,
- sampleRate,
- format,
- channelCount,
- frameCount,
- ((uint16_t)flags) << 16,
- sharedBuffer,
- output,
- &status);
+ mVolume[LEFT] = 1.0f;
+ mVolume[RIGHT] = 1.0f;
+ // create the IAudioTrack
+ status_t status = createTrack(streamType, sampleRate, format, channelCount,
+ frameCount, flags, sharedBuffer, output);
- if (track == 0) {
- LOGE("AudioFlinger could not create track, status: %d", status);
+ if (status != NO_ERROR) {
return status;
}
- sp<IMemory> cblk = track->getCblk();
- if (cblk == 0) {
- LOGE("Could not get control block");
- return NO_INIT;
- }
+
if (cbf != 0) {
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
if (mAudioTrackThread == 0) {
@@ -249,22 +232,6 @@
mStatus = NO_ERROR;
- mAudioTrack = track;
- mCblkMemory = cblk;
- mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
- mCblk->out = 1;
- // Update buffer size in case it has been limited by AudioFlinger during track creation
- mFrameCount = mCblk->frameCount;
- if (sharedBuffer == 0) {
- mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- } else {
- mCblk->buffers = sharedBuffer->pointer();
- // Force buffer full condition as data is already present in shared memory
- mCblk->stepUser(mFrameCount);
- }
- mCblk->volume[0] = mCblk->volume[1] = 0x1000;
- mVolume[LEFT] = 1.0f;
- mVolume[RIGHT] = 1.0f;
mStreamType = streamType;
mFormat = format;
mChannels = channels;
@@ -351,16 +318,27 @@
}
if (android_atomic_or(1, &mActive) == 0) {
- AudioSystem::startOutput(getOutput(), (AudioSystem::stream_type)mStreamType);
- mNewPosition = mCblk->server + mUpdatePeriod;
- mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
- mCblk->waitTimeMs = 0;
- if (t != 0) {
- t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
- } else {
- setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
+ audio_io_handle_t output = AudioTrack::getOutput();
+ status_t status = mAudioTrack->start();
+ if (status == DEAD_OBJECT) {
+ LOGV("start() dead IAudioTrack: creating a new one");
+ status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount,
+ mFrameCount, mFlags, mSharedBuffer, output);
}
- mAudioTrack->start();
+ if (status == NO_ERROR) {
+ AudioSystem::startOutput(output, (AudioSystem::stream_type)mStreamType);
+ mNewPosition = mCblk->server + mUpdatePeriod;
+ mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
+ mCblk->waitTimeMs = 0;
+ if (t != 0) {
+ t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
+ } else {
+ setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
+ }
+ } else {
+ LOGV("start() failed");
+ android_atomic_and(~1, &mActive);
+ }
}
if (t != 0) {
@@ -617,10 +595,67 @@
// -------------------------------------------------------------------------
+status_t AudioTrack::createTrack(
+ int streamType,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags,
+ const sp<IMemory>& sharedBuffer,
+ audio_io_handle_t output)
+{
+ status_t status;
+ const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
+ if (audioFlinger == 0) {
+ LOGE("Could not get audioflinger");
+ return NO_INIT;
+ }
+
+ sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),
+ streamType,
+ sampleRate,
+ format,
+ channelCount,
+ frameCount,
+ ((uint16_t)flags) << 16,
+ sharedBuffer,
+ output,
+ &status);
+
+ if (track == 0) {
+ LOGE("AudioFlinger could not create track, status: %d", status);
+ return status;
+ }
+ sp<IMemory> cblk = track->getCblk();
+ if (cblk == 0) {
+ LOGE("Could not get control block");
+ return NO_INIT;
+ }
+ mAudioTrack.clear();
+ mAudioTrack = track;
+ mCblkMemory.clear();
+ mCblkMemory = cblk;
+ mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());
+ mCblk->out = 1;
+ // Update buffer size in case it has been limited by AudioFlinger during track creation
+ mFrameCount = mCblk->frameCount;
+ if (sharedBuffer == 0) {
+ mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
+ } else {
+ mCblk->buffers = sharedBuffer->pointer();
+ // Force buffer full condition as data is already present in shared memory
+ mCblk->stepUser(mFrameCount);
+ }
+
+ mCblk->volumeLR = (int32_t(int16_t(mVolume[LEFT] * 0x1000)) << 16) | int16_t(mVolume[RIGHT] * 0x1000);
+
+ return NO_ERROR;
+}
+
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
int active;
- int timeout = 0;
status_t result;
audio_track_cblk_t* cblk = mCblk;
uint32_t framesReq = audioBuffer->frameCount;
@@ -632,17 +667,20 @@
uint32_t framesAvail = cblk->framesAvailable();
if (framesAvail == 0) {
- Mutex::Autolock _l(cblk->lock);
+ cblk->lock.lock();
goto start_loop_here;
while (framesAvail == 0) {
active = mActive;
if (UNLIKELY(!active)) {
LOGV("Not active and NO_MORE_BUFFERS");
+ cblk->lock.unlock();
return NO_MORE_BUFFERS;
}
- if (UNLIKELY(!waitCount))
+ if (UNLIKELY(!waitCount)) {
+ cblk->lock.unlock();
return WOULD_BLOCK;
- timeout = 0;
+ }
+
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
if (__builtin_expect(result!=NO_ERROR, false)) {
cblk->waitTimeMs += waitTimeMs;
@@ -654,14 +692,23 @@
"user=%08x, server=%08x", this, cblk->user, cblk->server);
//unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
cblk->lock.unlock();
- mAudioTrack->start();
+ result = mAudioTrack->start();
+ if (result == DEAD_OBJECT) {
+ LOGW("obtainBuffer() dead IAudioTrack: creating a new one");
+ result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,
+ mFrameCount, mFlags, mSharedBuffer, getOutput());
+ if (result == NO_ERROR) {
+ cblk = mCblk;
+ cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ }
+ }
cblk->lock.lock();
- timeout = 1;
}
cblk->waitTimeMs = 0;
}
if (--waitCount == 0) {
+ cblk->lock.unlock();
return TIMED_OUT;
}
}
@@ -669,6 +716,7 @@
start_loop_here:
framesAvail = cblk->framesAvailable_l();
}
+ cblk->lock.unlock();
}
cblk->waitTimeMs = 0;
@@ -684,11 +732,6 @@
framesReq = bufferEnd - u;
}
- LOGW_IF(timeout,
- "*** SERIOUS WARNING *** obtainBuffer() timed out "
- "but didn't need to be locked. We recovered, but "
- "this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server);
-
audioBuffer->flags = mMuted ? Buffer::MUTE : 0;
audioBuffer->channelCount = mChannelCount;
audioBuffer->frameCount = framesReq;
@@ -991,7 +1034,7 @@
// Mark that we have read the first buffer so that next time stepUser() is called
// we switch to normal obtainBuffer() timeout period
if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) {
- bufferTimeoutMs = MAX_RUN_TIMEOUT_MS - 1;
+ bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1;
}
// It is possible that we receive a flush()
// while the mixer is processing a block: in this case,
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index dacf75a..ba0d55b 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -15,6 +15,10 @@
** limitations under the License.
*/
+#define LOG_TAG "IAudioRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <stdint.h>
#include <sys/types.h>
@@ -42,8 +46,13 @@
{
Parcel data, reply;
data.writeInterfaceToken(IAudioRecord::getInterfaceDescriptor());
- remote()->transact(START, data, &reply);
- return reply.readInt32();
+ status_t status = remote()->transact(START, data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ } else {
+ LOGW("start() error: %s", strerror(-status));
+ }
+ return status;
}
virtual void stop()
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index 7f43347..01ffd75 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -15,6 +15,10 @@
** limitations under the License.
*/
+#define LOG_TAG "IAudioTrack"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
#include <stdint.h>
#include <sys/types.h>
@@ -45,8 +49,13 @@
{
Parcel data, reply;
data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
- remote()->transact(START, data, &reply);
- return reply.readInt32();
+ status_t status = remote()->transact(START, data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ } else {
+ LOGW("start() error: %s", strerror(-status));
+ }
+ return status;
}
virtual void stop()
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 91d0d00..60e3d71 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -798,7 +798,7 @@
// none
//
////////////////////////////////////////////////////////////////////////////////
-ToneGenerator::ToneGenerator(int streamType, float volume) {
+ToneGenerator::ToneGenerator(int streamType, float volume, bool threadCanCallJava) {
LOGV("ToneGenerator constructor: streamType=%d, volume=%f\n", streamType, volume);
@@ -808,6 +808,7 @@
LOGE("Unable to marshal AudioFlinger");
return;
}
+ mThreadCanCallJava = threadCanCallJava;
mStreamType = streamType;
mVolume = volume;
mpAudioTrack = 0;
@@ -1015,15 +1016,25 @@
}
// Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
- mpAudioTrack
- = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0);
-
+ mpAudioTrack = new AudioTrack();
if (mpAudioTrack == 0) {
LOGE("AudioTrack allocation failed");
goto initAudioTrack_exit;
}
LOGV("Create Track: %p\n", mpAudioTrack);
+ mpAudioTrack->set(mStreamType,
+ 0,
+ AudioSystem::PCM_16_BIT,
+ AudioSystem::CHANNEL_OUT_MONO,
+ 0,
+ 0,
+ audioCallback,
+ this,
+ 0,
+ 0,
+ mThreadCanCallJava);
+
if (mpAudioTrack->initCheck() != NO_ERROR) {
LOGE("AudioTrack->initCheck failed");
goto initAudioTrack_exit;
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 754e6e5..c3b591e 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1260,9 +1260,20 @@
// ----- Restore handling -----
- private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
+ private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
+ // If the target resides on the system partition, we allow it to restore
+ // data from the like-named package in a restore set even if the signatures
+ // do not match. (Unlike general applications, those flashed to the system
+ // partition will be signed with the device's platform certificate, so on
+ // different phones the same system app will have different signatures.)
+ if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (DEBUG) Log.v(TAG, "System app " + target.packageName + " - skipping sig check");
+ return true;
+ }
+
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
+ Signature[] deviceSigs = target.signatures;
if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs
+ " device=" + deviceSigs);
if ((storedSigs == null || storedSigs.length == 0)
@@ -1465,7 +1476,7 @@
continue;
}
- if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+ if (!signaturesMatch(metaInfo.signatures, packageInfo)) {
Log.w(TAG, "Signature mismatch restoring " + packageName);
EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
"Signature mismatch");
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index b1d58ce..3e3cf06 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -52,6 +52,7 @@
static final int LIGHT_FLASH_NONE = 0;
static final int LIGHT_FLASH_TIMED = 1;
+ static final int LIGHT_FLASH_HARDWARE = 2;
private final LinkedList<Vibration> mVibrations;
private Vibration mCurrentVibration;
@@ -125,7 +126,7 @@
mVibrations = new LinkedList<Vibration>();
mBatteryStats = BatteryStatsService.getService();
-
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mIntentReceiver, filter);
@@ -239,15 +240,15 @@
Binder.restoreCallingIdentity(identity);
}
}
-
+
public boolean getFlashlightEnabled() {
return Hardware.getFlashlightEnabled();
}
-
+
public void setFlashlightEnabled(boolean on) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
!= PackageManager.PERMISSION_GRANTED &&
- mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
+ mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission");
}
@@ -255,9 +256,9 @@
}
public void enableCameraFlash(int milliseconds) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED &&
- mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
+ mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires CAMERA or HARDWARE_TEST permission");
}
@@ -282,13 +283,13 @@
setLight_native(mNativePointer, light, color, mode, onMS, offMS);
}
- public void setAttentionLight(boolean on) {
+ public void setAttentionLight(boolean on, int color) {
// Not worthy of a permission. We shouldn't have a flashlight permission.
synchronized (this) {
mAttentionLightOn = on;
mPulsing = false;
- setLight_native(mNativePointer, LIGHT_ID_ATTENTION, on ? 0xffffffff : 0,
- LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color,
+ LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0);
}
}
@@ -302,8 +303,8 @@
}
if (!mAttentionLightOn && !mPulsing) {
mPulsing = true;
- setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0xff101010,
- LIGHT_FLASH_NONE, 0, 0);
+ setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff,
+ LIGHT_FLASH_HARDWARE, 7, 0);
mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
}
}
@@ -391,7 +392,7 @@
private class VibrateThread extends Thread {
final Vibration mVibration;
boolean mDone;
-
+
VibrateThread(Vibration vib) {
mVibration = vib;
mWakeLock.acquire();
@@ -425,7 +426,7 @@
long duration = 0;
while (!mDone) {
- // add off-time duration to any accumulated on-time duration
+ // add off-time duration to any accumulated on-time duration
if (index < len) {
duration += pattern[index++];
}
@@ -478,7 +479,7 @@
}
}
};
-
+
private static native int init_native();
private static native void finalize_native(int ptr);
@@ -489,7 +490,7 @@
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStats;
-
+
volatile VibrateThread mThread;
private int mNativePointer;
diff --git a/tests/AndroidTests/res/raw/v30_comma_separated.vcf b/tests/AndroidTests/res/raw/v30_comma_separated.vcf
new file mode 100644
index 0000000..98a7f20
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v30_comma_separated.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD
+VERSION:3.0
+N:F;G;M;;
+TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com
+END:VCARD
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
index 933ff2a..917b18e 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNodesVerifier.java
@@ -125,22 +125,18 @@
}
public PropertyNodesVerifierElem addNodeWithOrder(String propName, String propValue,
+ ContentValues contentValues) {
+ return addNodeWithOrder(propName, propValue, null, null, contentValues, null, null);
+ }
+
+ public PropertyNodesVerifierElem addNodeWithOrder(String propName, String propValue,
List<String> propValueList) {
return addNodeWithOrder(propName, propValue, propValueList, null, null, null, null);
}
public PropertyNodesVerifierElem addNodeWithOrder(String propName, List<String> propValueList) {
- StringBuffer buffer = new StringBuffer();
- boolean first = true;
- for (String propValueElem : propValueList) {
- if (first) {
- first = false;
- } else {
- buffer.append(';');
- }
- buffer.append(propValueElem);
- }
- return addNodeWithOrder(propName, buffer.toString(), propValueList,
+ final String propValue = concatinateListWithSemiColon(propValueList);
+ return addNodeWithOrder(propName, propValue.toString(), propValueList,
null, null, null, null);
}
@@ -149,6 +145,13 @@
return addNodeWithOrder(propName, propValue, null, null, null, paramMap_TYPE, null);
}
+ public PropertyNodesVerifierElem addNodeWithOrder(String propName,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ final String propValue = concatinateListWithSemiColon(propValueList);
+ return addNodeWithOrder(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
public PropertyNodesVerifierElem addNodeWithOrder(String propName, String propValue,
List<String> propValueList, TypeSet paramMap_TYPE) {
return addNodeWithOrder(propName, propValue, propValueList, null, null,
@@ -177,22 +180,18 @@
}
public PropertyNodesVerifierElem addNodeWithoutOrder(String propName, String propValue,
+ ContentValues contentValues) {
+ return addNodeWithoutOrder(propName, propValue, null, null, contentValues, null, null);
+ }
+
+ public PropertyNodesVerifierElem addNodeWithoutOrder(String propName, String propValue,
List<String> propValueList) {
return addNodeWithoutOrder(propName, propValue, propValueList, null, null, null, null);
}
public PropertyNodesVerifierElem addNodeWithoutOrder(String propName, List<String> propValueList) {
- StringBuffer buffer = new StringBuffer();
- boolean first = true;
- for (String propValueElem : propValueList) {
- if (first) {
- first = false;
- } else {
- buffer.append(';');
- }
- buffer.append(propValueElem);
- }
- return addNodeWithoutOrder(propName, buffer.toString(), propValueList,
+ final String propValue = concatinateListWithSemiColon(propValueList);
+ return addNodeWithoutOrder(propName, propValue, propValueList,
null, null, null, null);
}
@@ -201,6 +200,13 @@
return addNodeWithoutOrder(propName, propValue, null, null, null, paramMap_TYPE, null);
}
+ public PropertyNodesVerifierElem addNodeWithoutOrder(String propName,
+ List<String> propValueList, TypeSet paramMap_TYPE) {
+ final String propValue = concatinateListWithSemiColon(propValueList);
+ return addNodeWithoutOrder(propName, propValue, propValueList, null, null,
+ paramMap_TYPE, null);
+ }
+
public PropertyNodesVerifierElem addNodeWithoutOrder(String propName, String propValue,
List<String> propValueList, TypeSet paramMap_TYPE) {
return addNodeWithoutOrder(propName, propValue, propValueList, null, null,
@@ -291,6 +297,21 @@
}
}
+ private String concatinateListWithSemiColon(List<String> array) {
+ StringBuffer buffer = new StringBuffer();
+ boolean first = true;
+ for (String propValueElem : array) {
+ if (first) {
+ first = false;
+ } else {
+ buffer.append(';');
+ }
+ buffer.append(propValueElem);
+ }
+
+ return buffer.toString();
+ }
+
private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode,
List<PropertyNode> expectedButDifferentValueList) {
final String propName = actualNode.propName;
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
index 1aa334a..169ea71 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardImporterTests.java
@@ -16,9 +16,7 @@
package com.android.unit_tests.vcard;
-import android.content.ContentValues;
import android.pim.vcard.VCardConfig;
-import android.pim.vcard.VCardParser_V21;
import android.pim.vcard.exception.VCardException;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Email;
@@ -35,7 +33,6 @@
import com.android.unit_tests.vcard.PropertyNodesVerifierElem.TypeSet;
import java.io.IOException;
-import java.io.InputStream;
import java.util.Arrays;
public class VCardImporterTests extends VCardTestsBase {
@@ -571,10 +568,6 @@
* Tests all the properties in a complicated vCard are correctly parsed by the VCardParser.
*/
public void testV21ComplicatedCase_Parsing() throws IOException, VCardException {
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- ContentValues contentValuesForPhoto = new ContentValues();
- contentValuesForPhoto.put("ENCODING", "BASE64");
PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
verifier.addPropertyNodesVerifierElem()
.addNodeWithOrder("VERSION", "2.1")
@@ -598,7 +591,7 @@
null, null, new TypeSet("WORK"), null)
.addNodeWithOrder("LABEL",
"100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America",
- null, null, contentValuesForQP, new TypeSet("WORK"), null)
+ null, null, mContentValuesForQP, new TypeSet("WORK"), null)
.addNodeWithOrder("ADR",
";;42 Plantation St.;Baytown;LA;30314;United States of America",
Arrays.asList("", "", "42 Plantation St.", "Baytown",
@@ -606,7 +599,7 @@
new TypeSet("HOME"), null)
.addNodeWithOrder("LABEL",
"42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America",
- null, null, contentValuesForQP,
+ null, null, mContentValuesForQP,
new TypeSet("HOME"), null)
.addNodeWithOrder("EMAIL", "forrestgump@walladalla.com",
new TypeSet("PREF", "INTERNET"))
@@ -614,9 +607,9 @@
.addNodeWithOrder("NOTE", "The following note is the example from RFC 2045.")
.addNodeWithOrder("NOTE",
"Now's the time for all folk to come to the aid of their country.",
- null, null, contentValuesForQP, null, null)
+ null, null, mContentValuesForQP, null, null)
.addNodeWithOrder("PHOTO", null,
- null, sPhotoByteArrayForComplicatedCase, contentValuesForPhoto,
+ null, sPhotoByteArrayForComplicatedCase, mContentValuesForBase64V21,
new TypeSet("JPEG"), null)
.addNodeWithOrder("X-ATTRIBUTE", "Some String")
.addNodeWithOrder("BDAY", "19800101")
@@ -764,17 +757,15 @@
// Though Japanese careers append ";;;;" at the end of the value of "SOUND",
// vCard 2.1/3.0 specification does not allow multiple values.
// Do not need to handle it as multiple values.
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
verifier.addPropertyNodesVerifierElem()
.addNodeWithOrder("VERSION", "2.1", null, null, null, null, null)
.addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
- null, contentValuesForShiftJis, null, null)
+ null, mContentValuesForSJis, null, null)
.addNodeWithOrder("SOUND",
"\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
- null, null, contentValuesForShiftJis,
+ null, null, mContentValuesForSJis,
new TypeSet("X-IRMC-N"), null)
.addNodeWithOrder("TEL", "0300000000", null, null, null,
new TypeSet("VOICE", "PREF"), null);
@@ -831,23 +822,18 @@
}
public void testV21Japanese2_Parsing() throws IOException, VCardException {
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- ContentValues contentValuesForQPAndSJ = new ContentValues();
- contentValuesForQPAndSJ.put("ENCODING", "QUOTED-PRINTABLE");
- contentValuesForQPAndSJ.put("CHARSET", "SHIFT_JIS");
PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
verifier.addPropertyNodesVerifierElem()
.addNodeWithOrder("VERSION", "2.1")
.addNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
"", "", ""),
- null, contentValuesForShiftJis, null, null)
+ null, mContentValuesForSJis, null, null)
.addNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
- null, null, contentValuesForShiftJis, null, null)
+ null, null, mContentValuesForSJis, null, null)
.addNodeWithOrder("SOUND",
"\uFF71\uFF9D\uFF84\uFF9E\uFF73;\uFF9B\uFF72\uFF84\uFF9E\u0031;;;",
- null, null, contentValuesForShiftJis,
+ null, null, mContentValuesForSJis,
new TypeSet("X-IRMC-N"), null)
.addNodeWithOrder("ADR",
";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
@@ -859,15 +845,16 @@
"\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
"\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
"\u0036\u968E", "", "", "", "150-8512", ""),
- null, contentValuesForQPAndSJ, new TypeSet("HOME"), null)
+ null, mContentValuesForQPAndSJis, new TypeSet("HOME"), null)
.addNodeWithOrder("NOTE", "\u30E1\u30E2", null, null,
- contentValuesForQPAndSJ, null, null);
+ mContentValuesForQPAndSJis, null, null);
verifier.verify(R.raw.v21_japanese_2, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
}
public void testV21Japanese2_Type_Generic_Utf8() throws IOException, VCardException {
- ImportVerifierElem verifier = new ImportVerifierElem();
- verifier.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ ImportVerifier verifier = new ImportVerifier();
+ ImportVerifierElem elem = verifier.addImportVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
.put(StructuredName.FAMILY_NAME, "\u5B89\u85E4")
.put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9\u0031")
.put(StructuredName.DISPLAY_NAME,
@@ -877,7 +864,7 @@
.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF71\uFF9D\uFF84\uFF9E\uFF73")
.put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF9B\uFF72\uFF84\uFF9E\u0031");
- verifier.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+ elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
.put(StructuredPostal.POSTCODE, "150-8512")
.put(StructuredPostal.NEIGHBORHOOD,
"\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
@@ -890,23 +877,21 @@
"\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
"\u0036\u968E 150-8512")
.put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
- verifier.addExpected(Note.CONTENT_ITEM_TYPE)
+ elem.addExpected(Note.CONTENT_ITEM_TYPE)
.put(Note.NOTE, "\u30E1\u30E2");
verifier.verify(R.raw.v21_japanese_2, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
}
public void testV21MultipleEntryCase_Parse() throws IOException, VCardException {
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
verifier.addPropertyNodesVerifierElem()
.addNodeWithOrder("VERSION", "2.1")
.addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
- null, contentValuesForShiftJis, null, null)
+ null, mContentValuesForSJis, null, null)
.addNodeWithOrder("SOUND",
"\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
- null, null, contentValuesForShiftJis,
+ null, null, mContentValuesForSJis,
new TypeSet("X-IRMC-N"), null)
.addNodeWithOrder("TEL", "9", new TypeSet("X-NEC-SECRET"))
.addNodeWithOrder("TEL", "10", new TypeSet("X-NEC-HOTEL"))
@@ -917,10 +902,10 @@
.addNodeWithOrder("VERSION", "2.1")
.addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
- null, contentValuesForShiftJis, null, null)
+ null, mContentValuesForSJis, null, null)
.addNodeWithOrder("SOUND",
"\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
- null, null, contentValuesForShiftJis,
+ null, null, mContentValuesForSJis,
new TypeSet("X-IRMC-N"), null)
.addNodeWithOrder("TEL", "13", new TypeSet("MODEM"))
.addNodeWithOrder("TEL", "14", new TypeSet("PAGER"))
@@ -931,10 +916,10 @@
.addNodeWithOrder("VERSION", "2.1")
.addNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
- null, contentValuesForShiftJis, null, null)
+ null, mContentValuesForSJis, null, null)
.addNodeWithOrder("SOUND",
"\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
- null, null, contentValuesForShiftJis,
+ null, null, mContentValuesForSJis,
new TypeSet("X-IRMC-N"), null)
.addNodeWithOrder("TEL", "17", new TypeSet("X-NEC-BOY"))
.addNodeWithOrder("TEL", "18", new TypeSet("X-NEC-FRIEND"))
@@ -1013,4 +998,28 @@
.put(Phone.NUMBER, "20");
verifier.verify(R.raw.v21_multiple_entry, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
}
+
+ public void testPagerV30_Parse() throws IOException, VCardException {
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(this);
+ verifier.addPropertyNodesVerifierElem()
+ .addNodeWithOrder("VERSION", "3.0")
+ .addNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
+ .addNodeWithOrder("TEL", "6101231234@pagersample.com",
+ new TypeSet("WORK", "MSG", "PAGER"));
+ verifier.verify(R.raw.v30_comma_separated, VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8);
+ }
+
+ public void testPagerV30() throws IOException, VCardException {
+ ImportVerifier verifier = new ImportVerifier();
+ ImportVerifierElem elem = verifier.addImportVerifierElem();
+ elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "F")
+ .put(StructuredName.MIDDLE_NAME, "M")
+ .put(StructuredName.GIVEN_NAME, "G")
+ .put(StructuredName.DISPLAY_NAME, "G M F");
+ elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+ .put(Phone.TYPE, Phone.TYPE_PAGER)
+ .put(Phone.NUMBER, "6101231234@pagersample.com");
+ verifier.verify(R.raw.v30_comma_separated, VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8);
+ }
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java
new file mode 100644
index 0000000..4b65008
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardJapanizationTests.java
@@ -0,0 +1,109 @@
+/*
+ * 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.unit_tests.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+
+import com.android.unit_tests.vcard.PropertyNodesVerifierElem.TypeSet;
+
+import java.util.Arrays;
+
+public class VCardJapanizationTests extends VCardTestsBase {
+ private void testNameUtf8Common(int vcardType) {
+ ExportTestResolver resolver = new ExportTestResolver();
+ resolver.buildContactEntry().buildData(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+ .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+ .put(StructuredName.MIDDLE_NAME, "B")
+ .put(StructuredName.PREFIX, "Dr.")
+ .put(StructuredName.SUFFIX, "Ph.D");
+
+ VCardVerifier verifier = new VCardVerifier(resolver, vcardType);
+ ContentValues contentValues =
+ (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8);
+ verifier.addPropertyNodesVerifierElem()
+ .addNodeWithoutOrder("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
+ contentValues)
+ .addNodeWithoutOrder("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
+ Arrays.asList(
+ "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
+ null, contentValues, null, null);
+ verifier.verify();
+ }
+
+ public void testNameUtf8V21() {
+ testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+ }
+
+ public void testNameUtf8V30() {
+ testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+ }
+
+ public void testNameShiftJis() {
+ ExportTestResolver resolver = new ExportTestResolver();
+ resolver.buildContactEntry().buildData(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+ .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+ .put(StructuredName.MIDDLE_NAME, "B")
+ .put(StructuredName.PREFIX, "Dr.")
+ .put(StructuredName.SUFFIX, "Ph.D");
+
+ VCardVerifier verifier = new VCardVerifier(resolver,
+ VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+ verifier.addPropertyNodesVerifierElem()
+ .addNodeWithoutOrder("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
+ mContentValuesForSJis)
+ .addNodeWithoutOrder("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
+ Arrays.asList(
+ "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
+ null, mContentValuesForSJis, null, null);
+ verifier.verify();
+ }
+
+ /**
+ * DoCoMo phones require all name elements should be in "family name" field.
+ */
+ public void testNameDoCoMo() {
+ ExportTestResolver resolver = new ExportTestResolver();
+ resolver.buildContactEntry().buildData(StructuredName.CONTENT_ITEM_TYPE)
+ .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+ .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+ .put(StructuredName.MIDDLE_NAME, "B")
+ .put(StructuredName.PREFIX, "Dr.")
+ .put(StructuredName.SUFFIX, "Ph.D");
+
+ VCardVerifier verifier = new VCardVerifier(resolver,
+ VCardConfig.VCARD_TYPE_DOCOMO);
+ final String fullName = "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D";
+ verifier.addPropertyNodesVerifierElem()
+ .addNodeWithoutOrder("N", fullName + ";;;;",
+ Arrays.asList(fullName, "", "", "", ""),
+ null, mContentValuesForSJis, null, null)
+ .addNodeWithoutOrder("FN", fullName, mContentValuesForSJis)
+ .addNodeWithoutOrder("SOUND", ";;;;", new TypeSet("X-IRMC-N"))
+ .addNodeWithoutOrder("TEL", "", new TypeSet("HOME"))
+ .addNodeWithoutOrder("EMAIL", "", new TypeSet("HOME"))
+ .addNodeWithoutOrder("ADR", "", new TypeSet("HOME"))
+ .addNodeWithoutOrder("X-CLASS", "PUBLIC")
+ .addNodeWithoutOrder("X-REDUCTION", "")
+ .addNodeWithoutOrder("X-NO", "")
+ .addNodeWithoutOrder("X-DCM-HMN-MODE", "");
+ verifier.verify();
+ }
+}
\ No newline at end of file
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java
index 9ec6e05..bd4d13a 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTestsBase.java
@@ -66,7 +66,6 @@
import android.test.mock.MockContext;
import android.test.mock.MockCursor;
import android.text.TextUtils;
-import android.util.Log;
import junit.framework.TestCase;
@@ -185,6 +184,35 @@
public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8;
public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8;
+ // Do not modify these during tests.
+ protected final ContentValues mContentValuesForQP;
+ protected final ContentValues mContentValuesForSJis;
+ protected final ContentValues mContentValuesForUtf8;
+ protected final ContentValues mContentValuesForQPAndSJis;
+ protected final ContentValues mContentValuesForQPAndUtf8;
+ protected final ContentValues mContentValuesForBase64V21;
+ protected final ContentValues mContentValuesForBase64V30;
+
+ public VCardTestsBase() {
+ super();
+ mContentValuesForQP = new ContentValues();
+ mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ mContentValuesForSJis = new ContentValues();
+ mContentValuesForSJis.put("CHARSET", "SHIFT_JIS");
+ mContentValuesForUtf8 = new ContentValues();
+ mContentValuesForUtf8.put("CHARSET", "UTF-8");
+ mContentValuesForQPAndSJis = new ContentValues();
+ mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE");
+ mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS");
+ mContentValuesForQPAndUtf8 = new ContentValues();
+ mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE");
+ mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8");
+ mContentValuesForBase64V21 = new ContentValues();
+ mContentValuesForBase64V21.put("ENCODING", "BASE64");
+ mContentValuesForBase64V30 = new ContentValues();
+ mContentValuesForBase64V30.put("ENCODING", "b");
+ }
+
public class ImportTestResolver extends MockContentResolver {
ImportTestProvider mProvider = new ImportTestProvider();
@Override
@@ -834,7 +862,9 @@
InputStream is = null;
try {
- is = new ByteArrayInputStream(vcard.getBytes("UTF-8"));
+ String charset =
+ (VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
+ is = new ByteArrayInputStream(vcard.getBytes(charset));
testCase.assertEquals(true, parser.parse(is, null, builder));
} catch (IOException e) {
testCase.fail("Unexpected IOException: " + e.getMessage());