diff options
74 files changed, 2521 insertions, 1810 deletions
@@ -348,7 +348,7 @@ framework_docs_LOCAL_JAVA_LIBRARIES := \ framework \ framework_docs_LOCAL_MODULE_CLASS := JAVA_LIBRARIES -framework_docs_LOCAL_DROIDDOC_HTML_DIR := docs/html +framework_docs_LOCAL_DROIDDOC_HTML_DIR := $(LOCAL_PATH)/docs/html $(OUT_DOCS)/gen # The since flag (-since N.xml API_LEVEL) is used to add API Level information # to the reference documentation. Must be in order of oldest to newest. framework_docs_LOCAL_DROIDDOC_OPTIONS := \ @@ -539,8 +539,8 @@ LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk include $(BUILD_DROIDDOC) -# explicitly specify that online-sdk depends on framework-res. -$(full_target): framework-res-package-target +# explicitly specify that online-sdk depends on framework-res and any generated docs +$(full_target): framework-res-package-target $(ALL_GENERATED_DOCS) # ==== docs that have all of the stuff that's @hidden ======================= include $(CLEAR_VARS) diff --git a/api/current.xml b/api/current.xml index 38756d3..4b0068d 100644 --- a/api/current.xml +++ b/api/current.xml @@ -45115,6 +45115,17 @@ <parameter name="key" type="java.lang.String"> </parameter> </method> +<method name="startCommit" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> </interface> <interface name="SharedPreferences.OnSharedPreferenceChangeListener" abstract="true" diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 9a55a6f..1d1b4dc 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1248,8 +1248,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case IS_USER_A_MONKEY_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - reply.writeInt(isUserAMonkey() ? 1 : 0); + boolean areThey = isUserAMonkey(); reply.writeNoException(); + reply.writeInt(areThey ? 1 : 0); return true; } @@ -1263,8 +1264,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case IS_IMMERSIVE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - reply.writeInt(isImmersive(token) ? 1 : 0); + boolean isit = isImmersive(token); reply.writeNoException(); + reply.writeInt(isit ? 1 : 0); return true; } @@ -1279,8 +1281,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); - reply.writeInt(isTopActivityImmersive() ? 1 : 0); + boolean isit = isTopActivityImmersive(); reply.writeNoException(); + reply.writeInt(isit ? 1 : 0); return true; } @@ -1295,6 +1298,40 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case NEW_URI_PERMISSION_OWNER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String name = data.readString(); + IBinder perm = newUriPermissionOwner(name); + reply.writeNoException(); + reply.writeStrongBinder(perm); + return true; + } + + case GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder owner = data.readStrongBinder(); + int fromUid = data.readInt(); + String targetPkg = data.readString(); + Uri uri = Uri.CREATOR.createFromParcel(data); + int mode = data.readInt(); + grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode); + reply.writeNoException(); + return true; + } + + case REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder owner = data.readStrongBinder(); + Uri uri = null; + if (data.readInt() != 0) { + Uri.CREATOR.createFromParcel(data); + } + int mode = data.readInt(); + revokeUriPermissionFromOwner(owner, uri, mode); + reply.writeNoException(); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -2841,8 +2878,8 @@ class ActivityManagerProxy implements IActivityManager data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); mRemote.transact(IS_IMMERSIVE_TRANSACTION, data, reply, 0); - boolean res = reply.readInt() == 1; reply.readException(); + boolean res = reply.readInt() == 1; data.recycle(); reply.recycle(); return res; @@ -2854,8 +2891,8 @@ class ActivityManagerProxy implements IActivityManager Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); mRemote.transact(IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION, data, reply, 0); - boolean res = reply.readInt() == 1; reply.readException(); + boolean res = reply.readInt() == 1; data.recycle(); reply.recycle(); return res; @@ -2875,6 +2912,55 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } + + public IBinder newUriPermissionOwner(String name) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(name); + mRemote.transact(NEW_URI_PERMISSION_OWNER_TRANSACTION, data, reply, 0); + reply.readException(); + IBinder res = reply.readStrongBinder(); + data.recycle(); + reply.recycle(); + return res; + } + + public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg, + Uri uri, int mode) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(owner); + data.writeInt(fromUid); + data.writeString(targetPkg); + uri.writeToParcel(data, 0); + data.writeInt(mode); + mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void revokeUriPermissionFromOwner(IBinder owner, Uri uri, + int mode) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(owner); + if (uri != null) { + data.writeInt(1); + uri.writeToParcel(data, 0); + } else { + data.writeInt(0); + } + data.writeInt(mode); + mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } private IBinder mRemote; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 2870c50..7b35e7f 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2840,6 +2840,11 @@ class ContextImpl extends Context { } } + public void startCommit() { + // TODO: implement + commit(); + } + public boolean commit() { boolean returnValue; @@ -2914,7 +2919,7 @@ class ContextImpl extends Context { public Editor edit() { return new EditorImpl(); } - + private FileOutputStream createFileOutputStream(File file) { FileOutputStream str = null; try { diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 416f289..664cf18 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -318,6 +318,12 @@ public interface IActivityManager extends IInterface { public void crashApplication(int uid, int initialPid, String packageName, String message) throws RemoteException; + public IBinder newUriPermissionOwner(String name) throws RemoteException; + public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg, + Uri uri, int mode) throws RemoteException; + public void revokeUriPermissionFromOwner(IBinder owner, Uri uri, + int mode) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -524,4 +530,7 @@ public interface IActivityManager extends IInterface { int SET_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+111; int IS_TOP_ACTIVITY_IMMERSIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+112; int CRASH_APPLICATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+113; + int NEW_URI_PERMISSION_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+114; + int GRANT_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+115; + int REVOKE_URI_PERMISSION_FROM_OWNER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+116; } diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index a15e29e..f1b1490 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -151,14 +151,47 @@ public interface SharedPreferences { * {@link SharedPreferences} object it is editing. This atomically * performs the requested modifications, replacing whatever is currently * in the SharedPreferences. - * + * * <p>Note that when two editors are modifying preferences at the same * time, the last one to call commit wins. - * + * + * <p>If you don't care about the return value and you're + * using this from your application's main thread, consider + * using {@link #startCommit} instead. + * * @return Returns true if the new values were successfully written * to persistent storage. */ boolean commit(); + + /** + * Commit your preferences changes back from this Editor to the + * {@link SharedPreferences} object it is editing. This atomically + * performs the requested modifications, replacing whatever is currently + * in the SharedPreferences. + * + * <p>Note that when two editors are modifying preferences at the same + * time, the last one to call commit wins. + * + * <p>Unlike {@link #commit}, which writes its preferences out + * to persistent storage synchronously, {@link #startCommit} + * commits its changes to the in-memory + * {@link SharedPreferences} immediately but starts an + * asynchronous commit to disk and you won't be notified of + * any failures. If another editor on this + * {@link SharedPreferences} does a regular {@link #commit} + * while a {@link #startCommit} is still outstanding, the + * {@link #commit} will block until all async commits are + * completed as well as the commit itself. + * + * <p>If you call this from an {@link android.app.Activity}, + * the base class will wait for any async commits to finish in + * its {@link android.app.Activity#onPause}.</p> + * + * @return Returns true if the new values were successfully written + * to persistent storage. + */ + void startCommit(); } /** diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 97921fe..0068724 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -34,11 +34,19 @@ import java.util.HashMap; import java.util.List; /** + * <p> * SensorManager lets you access the device's {@link android.hardware.Sensor * sensors}. Get an instance of this class by calling * {@link android.content.Context#getSystemService(java.lang.String) * Context.getSystemService()} with the argument * {@link android.content.Context#SENSOR_SERVICE}. + * </p> + * <p> + * Always make sure to disable sensors you don't need, especially when your + * activity is paused. Failing to do so can drain the battery in just a few + * hours. Note that the system will <i>not</i> disable sensors automatically when + * the screen turns off. + * </p> * * <pre class="prettyprint"> * public class SensorActivity extends Activity, implements SensorEventListener { @@ -48,13 +56,22 @@ import java.util.List; * public SensorActivity() { * mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); * mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + * } + * + * protected void onResume() { + * super.onResume(); * mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL); * } * + * protected void onPause() { + * super.onPause(); + * mSensorManager.unregisterListener(this); + * } + * * public void onAccuracyChanged(Sensor sensor, int accuracy) { * } * - * public abstract void onSensorChanged(SensorEvent event) { + * public void onSensorChanged(SensorEvent event) { * } * } * </pre> diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 40ed980..d20e89d 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -249,6 +249,8 @@ public final class MediaStore { private static final int MICRO_KIND = 3; private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA}; static final int DEFAULT_GROUP_ID = 0; + private static final Object sThumbBufLock = new Object(); + private static byte[] sThumbBuf; private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) { Bitmap bitmap = null; @@ -321,11 +323,15 @@ public final class MediaStore { long magic = thumbFile.getMagic(origId); if (magic != 0) { if (kind == MICRO_KIND) { - byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; - if (thumbFile.getMiniThumbFromFile(origId, data) != null) { - bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap == null) { - Log.w(TAG, "couldn't decode byte array."); + synchronized (sThumbBufLock) { + if (sThumbBuf == null) { + sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; + } + if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) { + bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length); + if (bitmap == null) { + Log.w(TAG, "couldn't decode byte array."); + } } } return bitmap; @@ -357,11 +363,15 @@ public final class MediaStore { // Assuming thumbnail has been generated, at least original image exists. if (kind == MICRO_KIND) { - byte[] data = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; - if (thumbFile.getMiniThumbFromFile(origId, data) != null) { - bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap == null) { - Log.w(TAG, "couldn't decode byte array."); + synchronized (sThumbBufLock) { + if (sThumbBuf == null) { + sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB]; + } + if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) { + bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length); + if (bitmap == null) { + Log.w(TAG, "couldn't decode byte array."); + } } } } else if (kind == MINI_KIND) { diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index e5985c1..194c013 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -348,6 +348,7 @@ public abstract class LayoutInflater { public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final AttributeSet attrs = Xml.asAttributeSet(parser); + Context lastContext = (Context)mConstructorArgs[0]; mConstructorArgs[0] = mContext; View result = root; @@ -432,6 +433,10 @@ public abstract class LayoutInflater { + ": " + e.getMessage()); ex.initCause(e); throw ex; + } finally { + // Don't retain static reference on context. + mConstructorArgs[0] = lastContext; + mConstructorArgs[1] = null; } return result; diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 402443c..7b6991f 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -1138,22 +1138,24 @@ public class ViewDebug { final View captureView = findView(root, parameter); Bitmap b = performViewCapture(captureView, false); - - if (b != null) { - BufferedOutputStream out = null; - try { - out = new BufferedOutputStream(clientStream, 32 * 1024); - b.compress(Bitmap.CompressFormat.PNG, 100, out); - out.flush(); - } finally { - if (out != null) { - out.close(); - } - b.recycle(); - } - } else { + + if (b == null) { Log.w("View", "Failed to create capture bitmap!"); - clientStream.close(); + // Send an empty one so that it doesn't get stuck waiting for + // something. + b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + } + + BufferedOutputStream out = null; + try { + out = new BufferedOutputStream(clientStream, 32 * 1024); + b.compress(Bitmap.CompressFormat.PNG, 100, out); + out.flush(); + } finally { + if (out != null) { + out.close(); + } + b.recycle(); } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 3e0187d..f8e60ce 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -3338,7 +3338,8 @@ public class WebView extends AbsoluteLayout setUpSelect(); if (mNativeClass != 0 && nativeWordSelection(x, y)) { nativeSetExtendSelection(); - getWebChromeClient().onSelectionStart(this); + WebChromeClient client = getWebChromeClient(); + if (client != null) client.onSelectionStart(this); return true; } notifySelectDialogDismissed(); @@ -4126,7 +4127,8 @@ public class WebView extends AbsoluteLayout */ public void selectionDone() { if (mSelectingText) { - getWebChromeClient().onSelectionDone(this); + WebChromeClient client = getWebChromeClient(); + if (client != null) client.onSelectionDone(this); invalidate(); // redraw without selection notifySelectDialogDismissed(); } diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 407d2e7..62a4495 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -506,7 +506,7 @@ static void blockSigpipe() static void readLocale(char* language, char* region) { char propLang[PROPERTY_VALUE_MAX], propRegn[PROPERTY_VALUE_MAX]; - + property_get("persist.sys.language", propLang, ""); property_get("persist.sys.country", propRegn, ""); if (*propLang == 0 && *propRegn == 0) { @@ -710,6 +710,33 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) LOGW("dalvik.vm.gc.overwritefree should be 'true' or 'false'"); } + /* enable heap verification before each gc */ + property_get("dalvik.vm.gc.preverify", propBuf, "false"); + if (strcmp(propBuf, "true") == 0) { + opt.optionString = "-Xgc:preverify"; + mOptions.add(opt); + } else if (strcmp(propBuf, "false") != 0) { + LOGW("dalvik.vm.gc.preverify should be 'true' or 'false'"); + } + + /* enable heap verification after each gc */ + property_get("dalvik.vm.gc.postverify", propBuf, "false"); + if (strcmp(propBuf, "true") == 0) { + opt.optionString = "-Xgc:postverify"; + mOptions.add(opt); + } else if (strcmp(propBuf, "false") != 0) { + LOGW("dalvik.vm.gc.postverify should be 'true' or 'false'"); + } + + /* enable card table verification for partial gc */ + property_get("dalvik.vm.gc.verifycardtable", propBuf, "false"); + if (strcmp(propBuf, "true") == 0) { + opt.optionString = "-Xgc:verifycardtable"; + mOptions.add(opt); + } else if (strcmp(propBuf, "false") != 0) { + LOGW("dalvik.vm.gc.verifycardtable should be 'true' or 'false'"); + } + /* enable debugging; set suspend=y to pause during VM init */ #ifdef HAVE_ANDROID_OS /* use android ADB transport */ @@ -757,16 +784,6 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) } #if defined(WITH_JIT) - /* Minimal profile threshold to trigger JIT compilation */ - char jitThresholdBuf[sizeof("-Xjitthreshold:") + PROPERTY_VALUE_MAX]; - property_get("dalvik.vm.jit.threshold", propBuf, ""); - if (strlen(propBuf) > 0) { - strcpy(jitThresholdBuf, "-Xjitthreshold:"); - strcat(jitThresholdBuf, propBuf); - opt.optionString = jitThresholdBuf; - mOptions.add(opt); - } - /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */ char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX]; property_get("dalvik.vm.jit.op", propBuf, ""); @@ -777,16 +794,6 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) mOptions.add(opt); } - /* - * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only - * for non-selected opcodes. - */ - property_get("dalvik.vm.jit.includeop", propBuf, ""); - if (strlen(propBuf) > 0) { - opt.optionString = "-Xincludeselectedop"; - mOptions.add(opt); - } - /* Force interpreter-only mode for selected methods */ char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX]; property_get("dalvik.vm.jit.method", propBuf, ""); @@ -796,37 +803,6 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) opt.optionString = jitMethodBuf; mOptions.add(opt); } - - /* - * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only - * for non-selected methods. - */ - property_get("dalvik.vm.jit.includemethod", propBuf, ""); - if (strlen(propBuf) > 0) { - opt.optionString = "-Xincludeselectedmethod"; - mOptions.add(opt); - } - - /* - * Enable profile collection on JIT'ed code. - */ - property_get("dalvik.vm.jit.profile", propBuf, ""); - if (strlen(propBuf) > 0) { - opt.optionString = "-Xjitprofile"; - mOptions.add(opt); - } - - /* - * Disable optimizations by setting the corresponding bit to 1. - */ - char jitOptBuf[sizeof("-Xjitdisableopt:") + PROPERTY_VALUE_MAX]; - property_get("dalvik.vm.jit.disableopt", propBuf, ""); - if (strlen(propBuf) > 0) { - strcpy(jitOptBuf, "-Xjitdisableopt:"); - strcat(jitOptBuf, propBuf); - opt.optionString = jitOptBuf; - mOptions.add(opt); - } #endif if (executionMode == kEMIntPortable) { diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java index 5968e83..951d0d8 100644 --- a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java +++ b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; +import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; import android.test.mock.MockCursor; @@ -44,6 +45,10 @@ import java.util.List; public ContactEntry addInputContactEntry() { return mProvider.buildInputEntry(); } + + public ExportTestProvider getProvider() { + return mProvider; + } } /* package */ class MockEntityIterator implements EntityIterator { diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java index c3f6f79..1563da9 100644 --- a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java +++ b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java @@ -19,8 +19,6 @@ import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentValues; import android.net.Uri; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Event; import android.provider.ContactsContract.CommonDataKinds.GroupMembership; @@ -34,6 +32,9 @@ import android.provider.ContactsContract.CommonDataKinds.Relation; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; import android.provider.ContactsContract.CommonDataKinds.Website; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.RawContacts; +import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; import android.text.TextUtils; @@ -45,10 +46,10 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; -import java.util.Map.Entry; /* package */ class ImportTestResolver extends MockContentResolver { final ImportTestProvider mProvider; diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java index 2de0464..2962a92 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java @@ -438,14 +438,26 @@ public class VCardExporterTests extends VCardTestsBase { .put(Phone.TYPE, Phone.TYPE_CUSTOM) .put(Phone.LABEL, "invalid"); PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); - elem.addExpectedNode("TEL", "1", new TypeSet("MODEM")) - .addExpectedNode("TEL", "2", new TypeSet("MSG")) - .addExpectedNode("TEL", "3", new TypeSet("BBS")) - .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) - .addExpectedNode("TEL", "5", new TypeSet("VOICE")) - .addExpectedNode("TEL", "6", new TypeSet("CELL")) - .addExpectedNode("TEL", "7", new TypeSet("CELL")) - .addExpectedNode("TEL", "8", new TypeSet("X-invalid")); + if (VCardConfig.isV30(vcardType)) { + // vCard 3.0 accepts "invalid". Also stop using toUpper() + elem.addExpectedNode("TEL", "1", new TypeSet("Modem")) + .addExpectedNode("TEL", "2", new TypeSet("MSG")) + .addExpectedNode("TEL", "3", new TypeSet("BBS")) + .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) + .addExpectedNode("TEL", "5", new TypeSet("VOICE")) + .addExpectedNode("TEL", "6", new TypeSet("CELL")) + .addExpectedNode("TEL", "7", new TypeSet("CELL")) + .addExpectedNode("TEL", "8", new TypeSet("invalid")); + } else { + elem.addExpectedNode("TEL", "1", new TypeSet("MODEM")) + .addExpectedNode("TEL", "2", new TypeSet("MSG")) + .addExpectedNode("TEL", "3", new TypeSet("BBS")) + .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) + .addExpectedNode("TEL", "5", new TypeSet("VOICE")) + .addExpectedNode("TEL", "6", new TypeSet("CELL")) + .addExpectedNode("TEL", "7", new TypeSet("CELL")) + .addExpectedNode("TEL", "8", new TypeSet("X-invalid")); + } } public void testPhoneTypeHandlingV21() { diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java index 416e872..9c6003f 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java @@ -16,95 +16,8 @@ package android.pim.vcard; -import android.content.ContentProvider; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; import android.content.ContentValues; -import android.content.EntityIterator; -import android.content.res.AssetFileDescriptor; -import android.database.Cursor; -import android.database.CursorWindow; -import android.database.IBulkCursor; -import android.database.IContentObserver; -import android.net.Uri; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.pim.vcard.VCardConfig; import android.test.AndroidTestCase; -import android.util.Log; - -import java.util.ArrayList; - -/** - * Almost a dead copy of android.test.mock.MockContentProvider, but different in that this - * class extends ContentProvider, not implementing IContentProvider, - * so that MockContentResolver is able to accept this class :( - */ -class MockContentProvider extends ContentProvider { - @Override - public boolean onCreate() { - return true; - } - - @Override - public int bulkInsert(Uri url, ContentValues[] initialValues) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @SuppressWarnings("unused") - public IBulkCursor bulkQuery(Uri url, String[] projection, String selection, - String[] selectionArgs, String sortOrder, IContentObserver observer, - CursorWindow window) throws RemoteException { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - @SuppressWarnings("unused") - public int delete(Uri url, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public String getType(Uri url) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Uri insert(Uri url, ContentValues initialValues) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public ParcelFileDescriptor openFile(Uri url, String mode) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public AssetFileDescriptor openAssetFile(Uri uri, String mode) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - public IBinder asBinder() { - throw new UnsupportedOperationException("unimplemented mock method"); - } -} /** * BaseClass for vCard unit tests with utility classes. diff --git a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java index bfc3158..3cb5b9b 100644 --- a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java +++ b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java @@ -31,6 +31,7 @@ import android.pim.vcard.VCardParser_V30; import android.pim.vcard.exception.VCardException; import android.test.AndroidTestCase; import android.test.mock.MockContext; +import android.util.Log; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -51,6 +52,8 @@ import java.util.Arrays; } /* package */ class VCardVerifier { + private static final String LOG_TAG = "VCardVerifier"; + private class VCardVerifierInternal implements VCardComposer.OneEntryHandler { public boolean onInit(Context context) { return true; @@ -267,10 +270,13 @@ import java.util.Arrays; final ContentResolver resolver, final Uri uri, final String selection, final String[] selectionArgs, final String sortOrder) { - final ContentProvider provider = - resolver.acquireContentProviderClient(uri).getLocalContentProvider(); - return ((ExportTestProvider)provider).queryEntities( - uri, selection, selectionArgs, sortOrder); + if (ExportTestResolver.class.equals(resolver.getClass())) { + return ((ExportTestResolver)resolver).getProvider().queryEntities( + uri, selection, selectionArgs, sortOrder); + } + + Log.e(LOG_TAG, "Unexpected provider given."); + return null; } private Method getMockGetEntityIteratorMethod() @@ -285,18 +291,19 @@ import java.util.Arrays; composer.addHandler(mLineVerifier); composer.addHandler(mVCardVerifierInternal); if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) { - mTestCase.fail("init() failed. Reason: " + composer.getErrorReason()); + AndroidTestCase.fail("init() failed. Reason: " + composer.getErrorReason()); } - mTestCase.assertFalse(composer.isAfterLast()); + AndroidTestCase.assertFalse(composer.isAfterLast()); try { while (!composer.isAfterLast()) { try { final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod(); - mTestCase.assertTrue( - composer.createOneEntry(getMockGetEntityIteratorMethod())); + AndroidTestCase.assertNotNull(mockGetEntityIteratorMethod); + AndroidTestCase.assertTrue( + composer.createOneEntry(mockGetEntityIteratorMethod)); } catch (Exception e) { e.printStackTrace(); - mTestCase.fail(); + AndroidTestCase.fail(); } } } finally { diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h index 53205bb..a3f094c 100644 --- a/include/media/EffectPresetReverbApi.h +++ b/include/media/EffectPresetReverbApi.h @@ -43,7 +43,8 @@ typedef enum REVERB_PRESET_LARGEROOM, REVERB_PRESET_MEDIUMHALL, REVERB_PRESET_LARGEHALL, - REVERB_PRESET_PLATE + REVERB_PRESET_PLATE, + REVERB_PRESET_LAST = REVERB_PRESET_PLATE } t_reverb_presets; #if __cplusplus diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h index 6f7dc38..9d2cff6 100644 --- a/include/media/stagefright/DataSource.h +++ b/include/media/stagefright/DataSource.h @@ -28,6 +28,7 @@ namespace android { +struct AMessage; class String8; class DataSource : public RefBase { @@ -59,10 +60,14 @@ public: //////////////////////////////////////////////////////////////////////////// - bool sniff(String8 *mimeType, float *confidence); + bool sniff(String8 *mimeType, float *confidence, sp<AMessage> *meta); + // The sniffer can optionally fill in "meta" with an AMessage containing + // a dictionary of values that helps the corresponding extractor initialize + // its state without duplicating effort already exerted by the sniffer. typedef bool (*SnifferFunc)( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *meta); static void RegisterSniffer(SnifferFunc func); static void RegisterDefaultSniffers(); diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp index 798271e..d856eb4 100644 --- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp +++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "Bundle" #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #include <cutils/log.h> #include <assert.h> diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp index 2043e44..03f1409 100755 --- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp +++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "Reverb" #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #include <cutils/log.h> #include <assert.h> @@ -61,42 +61,81 @@ namespace { /* Preset definitions */ /* */ /************************************************************************************/ -LVM_UINT16 RevPreset_Level[] = { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32}; -LVM_UINT16 RevPreset_LPF[] = {1298, 1000, 5012, 3542, 3400, 23999, 2536, 1000, 1000, 1000}; -LVM_UINT16 RevPreset_HPF[] = { 50, 50, 50, 50, 50, 50, 50, 50, 50, 50}; -LVM_UINT16 RevPreset_T60[] = {1490, 500, 2310, 4230, 3920, 2910, 7000, 1490, 1490, 170}; -LVM_UINT16 RevPreset_Density[] = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}; -LVM_UINT16 RevPreset_Damping[] = { 54, 10, 64, 59, 70, 100, 33, 54, 21, 10}; -LVM_UINT16 RevPreset_RoomSize[] = { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}; -/************************************************************************************/ -/* */ -/* Preset definitions */ -/* */ -/************************************************************************************/ -#define REV_PRESET_BATHROOM 0 -#define REV_PRESET_LIVINGROOM 1 -#define REV_PRESET_STONEROOM 2 -#define REV_PRESET_AUDITORIUM 3 -#define REV_PRESET_CONCERTHALL 4 -#define REV_PRESET_CAVE 5 -#define REV_PRESET_ARENA 6 -#define REV_PRESET_FOREST 7 -#define REV_PRESET_MOUNTAINS 8 -#define REV_PRESET_PADDEDCELL 9 - -// NXP SW Reverb UUID -const effect_descriptor_t gReverbDescriptor = { +const static t_reverb_settings sReverbPresets[] = { + // REVERB_PRESET_NONE: values are unused + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + // REVERB_PRESET_SMALLROOM + {-1000, -600, 1100, 830, -400, 5, 500, 10, 1000, 1000}, + // REVERB_PRESET_MEDIUMROOM + {-1000, -600, 1300, 830, -1000, 20, -200, 20, 1000, 1000}, + // REVERB_PRESET_LARGEROOM + {-1000, -600, 1500, 830, -1600, 5, -1000, 40, 1000, 1000}, + // REVERB_PRESET_MEDIUMHALL + {-1000, -600, 1800, 700, -1300, 15, -800, 30, 1000, 1000}, + // REVERB_PRESET_LARGEHALL + {-1000, -600, 1800, 700, -2000, 30, -1400, 60, 1000, 1000}, + // REVERB_PRESET_PLATE + {-1000, -200, 1300, 900, 0, 2, 0, 10, 1000, 750}, +}; + + +// NXP SW auxiliary environmental reverb +const effect_descriptor_t gAuxEnvReverbDescriptor = { { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e } }, { 0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, EFFECT_API_VERSION, - (EFFECT_FLAG_TYPE_AUXILIARY | EFFECT_FLAG_INSERT_LAST), + EFFECT_FLAG_TYPE_AUXILIARY, + 0, // TODO + 1, + "Auxiliary Environmental Reverb", + "NXP Software Ltd.", +}; + +// NXP SW insert environmental reverb +static const effect_descriptor_t gInsertEnvReverbDescriptor = { + {0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, {0x4e, 0x23, 0x4d, 0x06, 0x83, 0x9e}}, + {0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, 0, // TODO 1, - "Reverb", + "Insert Environmental Reverb", "NXP Software Ltd.", }; +// NXP SW auxiliary preset reverb +static const effect_descriptor_t gAuxPresetReverbDescriptor = { + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + EFFECT_FLAG_TYPE_AUXILIARY, + 0, // TODO + 1, + "Auxiliary Preset Reverb", + "NXP Software Ltd.", +}; + +// NXP SW insert preset reverb +static const effect_descriptor_t gInsertPresetReverbDescriptor = { + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + EFFECT_API_VERSION, + EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, + 0, // TODO + 1, + "Insert Preset Reverb", + "NXP Software Ltd.", +}; + +// gDescriptors contains pointers to all defined effect descriptor in this library +static const effect_descriptor_t * const gDescriptors[] = { + &gAuxEnvReverbDescriptor, + &gInsertEnvReverbDescriptor, + &gAuxPresetReverbDescriptor, + &gInsertPresetReverbDescriptor +}; + struct ReverbContext{ const struct effect_interface_s *itfe; effect_config_t config; @@ -114,8 +153,14 @@ struct ReverbContext{ FILE *PcmOutPtr; #endif LVM_Fs_en SampleRate; + bool auxiliary; + bool preset; + uint16_t curPreset; + uint16_t nextPreset; }; +#define REVERB_DEFAULT_PRESET REVERB_PRESET_MEDIUMROOM + //--- local function prototypes int Reverb_init (ReverbContext *pContext); void Reverb_free (ReverbContext *pContext); @@ -125,11 +170,12 @@ int Reverb_getParameter (ReverbContext *pContext, void *pParam, size_t *pValueSize, void *pValue); +int Reverb_LoadPreset (ReverbContext *pContext); /* Effect Library Interface Implementation */ extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects){ LOGV("\n\tEffectQueryNumberEffects start"); - *pNumEffects = 1; + *pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); LOGV("\tEffectQueryNumberEffects creating %d effects", *pNumEffects); LOGV("\tEffectQueryNumberEffects end\n"); return 0; @@ -142,11 +188,11 @@ extern "C" int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescripto LOGV("\tLVM_ERROR : EffectQueryEffect was passed NULL pointer"); return -EINVAL; } - if (index > 0){ + if (index >= sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)) { LOGV("\tLVM_ERROR : EffectQueryEffect index out of range %d", index); return -ENOENT; } - memcpy(pDescriptor, &gReverbDescriptor, sizeof(effect_descriptor_t)); + memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t)); LOGV("\tEffectQueryEffect end\n"); return 0; } /* end EffectQueryEffect */ @@ -157,6 +203,8 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, effect_interface_t *pInterface){ int ret; int i; + int length = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); + const effect_descriptor_t *desc; LOGV("\t\nEffectCreate start"); @@ -165,9 +213,16 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, return -EINVAL; } - if (memcmp(uuid, &gReverbDescriptor.uuid, sizeof(effect_uuid_t)) != 0){ - LOGV("\tLVM_ERROR : EffectCreate() invalid UUID"); - return -EINVAL; + for (i = 0; i < length; i++) { + desc = gDescriptors[i]; + if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) + == 0) { + break; + } + } + + if (i == length) { + return -ENOENT; } ReverbContext *pContext = new ReverbContext; @@ -175,6 +230,19 @@ extern "C" int EffectCreate(effect_uuid_t *uuid, pContext->itfe = &gReverbInterface; pContext->hInstance = NULL; + pContext->auxiliary = false; + if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY){ + pContext->auxiliary = true; + } + + pContext->preset = false; + if (memcmp(&desc->type, SL_IID_PRESETREVERB, sizeof(effect_uuid_t)) == 0) { + pContext->preset = true; + // force reloading preset at first call to process() + pContext->curPreset = REVERB_PRESET_LAST + 1; + pContext->nextPreset = REVERB_DEFAULT_PRESET; + } + LOGV("\tEffectCreate - Calling Reverb_init"); ret = Reverb_init(pContext); @@ -288,6 +356,14 @@ void From2iToMono_32( const LVM_INT32 *src, return; } + +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + //---------------------------------------------------------------------------- // process() //---------------------------------------------------------------------------- @@ -344,6 +420,9 @@ int process( LVM_INT16 *pIn, fflush(pContext->PcmInPtr); #endif + if (pContext->preset && pContext->nextPreset != pContext->curPreset) { + Reverb_LoadPreset(pContext); + } // Convert to Input 32 bits for(int i=0; i<frameCount*samplesPerFrame; i++){ InFrames32[i] = (LVM_INT32)pIn[i]<<8; @@ -359,18 +438,28 @@ int process( LVM_INT16 *pIn, //frameCount, pContext->config.inputCfg.channels, CHANNEL_MONO, //pContext->config.outputCfg.channels, CHANNEL_STEREO); + if (pContext->preset && pContext->curPreset == REVERB_PRESET_NONE) { + memset(OutFrames32, 0, frameCount * sizeof(LVM_INT32) * 2); + } else { /* Process the samples */ LvmStatus = LVREV_Process(pContext->hInstance, /* Instance handle */ InFrames32, /* Input buffer */ OutFrames32, /* Output buffer */ frameCount); /* Number of samples to read */ + } + + if (!pContext->auxiliary) { + for (int i=0; i<frameCount*2; i++){ + OutFrames32[i] += InFrames32[i]; + } + } LVM_ERROR_CHECK(LvmStatus, "LVREV_Process", "process") if(LvmStatus != LVREV_SUCCESS) return -EINVAL; // Convert to 16 bits for(int i=0; i<frameCount*2; i++){ // Always stereo - OutFrames16[i] = (LVM_INT16)(OutFrames32[i]>>8); + OutFrames16[i] = clamp16(OutFrames32[i]>>8); } #ifdef LVM_PCM @@ -382,7 +471,7 @@ int process( LVM_INT16 *pIn, if (pContext->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE){ //LOGV("\tBuffer access is ACCUMULATE"); for (int i=0; i<frameCount*2; i++){ - pOut[i] += OutFrames16[i]; + pOut[i] = clamp16((int32_t)pOut[i] + (int32_t)OutFrames16[i]); } }else{ //LOGV("\tBuffer access is WRITE"); @@ -462,6 +551,8 @@ int Reverb_configure(ReverbContext *pContext, effect_config_t *pConfig){ CHECK_ARG(pConfig->inputCfg.samplingRate == pConfig->outputCfg.samplingRate); CHECK_ARG(pConfig->inputCfg.format == pConfig->outputCfg.format); + CHECK_ARG((pContext->auxiliary && pConfig->inputCfg.channels == CHANNEL_MONO) || + ((!pContext->auxiliary) && pConfig->inputCfg.channels == CHANNEL_STEREO)); CHECK_ARG(pConfig->outputCfg.channels == CHANNEL_STEREO); CHECK_ARG(pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_WRITE || pConfig->outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); @@ -540,18 +631,8 @@ int Reverb_configure(ReverbContext *pContext, effect_config_t *pConfig){ int Reverb_init(ReverbContext *pContext){ int status; - int channel_mode; - LOGV("\tReverb_init start %d", gReverbDescriptor.flags); - - if((gReverbDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT){ - LOGV("\tReverb_init EFFECT_FLAG_TYPE_INSERT"); - channel_mode = CHANNEL_STEREO; - } - if((gReverbDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY ){ - LOGV("\tReverb_init EFFECT_FLAG_TYPE_AUXILIARY"); - channel_mode = CHANNEL_MONO; - } + LOGV("\tReverb_init start"); CHECK_ARG(pContext != NULL); @@ -560,7 +641,12 @@ int Reverb_init(ReverbContext *pContext){ } pContext->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; - pContext->config.inputCfg.channels = channel_mode; + if (pContext->auxiliary) { + pContext->config.inputCfg.channels = CHANNEL_MONO; + } else { + pContext->config.inputCfg.channels = CHANNEL_STEREO; + } + pContext->config.inputCfg.format = SAMPLE_FORMAT_PCM_S15; pContext->config.inputCfg.samplingRate = 44100; pContext->config.inputCfg.bufferProvider.getBuffer = NULL; @@ -653,11 +739,11 @@ int Reverb_init(ReverbContext *pContext){ /* Reverb parameters */ params.Level = 0; params.LPF = 23999; - params.HPF = RevPreset_HPF[REV_PRESET_MOUNTAINS]; - params.T60 = RevPreset_T60[REV_PRESET_MOUNTAINS]; - params.Density = RevPreset_Density[REV_PRESET_MOUNTAINS]; - params.Damping = RevPreset_Damping[REV_PRESET_MOUNTAINS]; - params.RoomSize = RevPreset_RoomSize[REV_PRESET_MOUNTAINS]; + params.HPF = 50; + params.T60 = 1490; + params.Density = 100; + params.Damping = 21; + params.RoomSize = 100; /* Saved strength is used to return the exact strength that was used in the set to the get * because we map the original strength range of 0:1000 to 1:15, and this will avoid @@ -1294,6 +1380,44 @@ int32_t ReverbGetDensity(ReverbContext *pContext){ } //---------------------------------------------------------------------------- +// Reverb_LoadPreset() +//---------------------------------------------------------------------------- +// Purpose: +// Load a the next preset +// +// Inputs: +// pContext - handle to instance data +// +// Outputs: +// +// Side Effects: +// +//---------------------------------------------------------------------------- +int Reverb_LoadPreset(ReverbContext *pContext) +{ + //TODO: add reflections delay, level and reverb delay when early reflections are + // implemented + pContext->curPreset = pContext->nextPreset; + + if (pContext->curPreset != REVERB_PRESET_NONE) { + const t_reverb_settings *preset = &sReverbPresets[pContext->curPreset]; + ReverbSetRoomLevel(pContext, preset->roomLevel); + ReverbSetRoomHfLevel(pContext, preset->roomHFLevel); + ReverbSetDecayTime(pContext, preset->decayTime); + ReverbSetDecayHfRatio(pContext, preset->decayHFRatio); + //reflectionsLevel + //reflectionsDelay + ReverbSetReverbLevel(pContext, preset->reverbLevel); + // reverbDelay + ReverbSetDiffusion(pContext, preset->diffusion); + ReverbSetDensity(pContext, preset->density); + } + + return 0; +} + + +//---------------------------------------------------------------------------- // Reverb_getParameter() //---------------------------------------------------------------------------- // Purpose: @@ -1325,6 +1449,15 @@ int Reverb_getParameter(ReverbContext *pContext, t_reverb_settings *pProperties; //LOGV("\tReverb_getParameter start"); + if (pContext->preset) { + if (param != REVERB_PARAM_PRESET || *pValueSize < sizeof(uint16_t)) { + return -EINVAL; + } + + *(uint16_t *)pValue = pContext->nextPreset; + LOGV("get REVERB_PARAM_PRESET, preset %d", pContext->nextPreset); + return 0; + } switch (param){ case REVERB_PARAM_ROOM_LEVEL: @@ -1531,6 +1664,18 @@ int Reverb_setParameter (ReverbContext *pContext, void *pParam, void *pValue){ int32_t param = *pParamTemp++; //LOGV("\tReverb_setParameter start"); + if (pContext->preset) { + if (param != REVERB_PARAM_PRESET) { + return -EINVAL; + } + + uint16_t preset = *(uint16_t *)pValue; + LOGV("set REVERB_PARAM_PRESET, preset %d", preset); + if (preset > REVERB_PRESET_LAST) { + return -EINVAL; + } + pContext->nextPreset = preset; + } switch (param){ case REVERB_PARAM_PROPERTIES: diff --git a/media/libmedia/fixedfft.cpp b/media/libmedia/fixedfft.cpp index 28eb05a..9cf05ba 100644 --- a/media/libmedia/fixedfft.cpp +++ b/media/libmedia/fixedfft.cpp @@ -26,7 +26,9 @@ #include <stdio.h> #include <stdint.h> +#ifdef __ARM_ARCH__ #include <machine/cpu-features.h> +#endif #define LOG_FFT_SIZE 10 #define MAX_FFT_SIZE (1 << LOG_FFT_SIZE) diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp index 70af2da..1b05528 100644 --- a/media/libstagefright/AMRExtractor.cpp +++ b/media/libstagefright/AMRExtractor.cpp @@ -87,7 +87,7 @@ AMRExtractor::AMRExtractor(const sp<DataSource> &source) mInitCheck(NO_INIT) { String8 mimeType; float confidence; - if (!SniffAMR(mDataSource, &mimeType, &confidence)) { + if (!SniffAMR(mDataSource, &mimeType, &confidence, NULL)) { return; } @@ -276,7 +276,8 @@ status_t AMRSource::read( //////////////////////////////////////////////////////////////////////////////// bool SniffAMR( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { char header[9]; if (source->readAt(0, header, sizeof(header)) != sizeof(header)) { diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 11fdf56..a0cd5c3 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -458,27 +458,34 @@ void AwesomePlayer::onBufferingUpdate() { return; } + bool eos; + size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos); + size_t lowWatermark = 400000; size_t highWatermark = 1000000; - off_t size; - if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) { - int64_t bitrate = size * 8000000ll / mDurationUs; // in bits/sec + if (eos) { + notifyListener_l(MEDIA_BUFFERING_UPDATE, 100); + } else { + off_t size; + if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) { + int64_t bitrate = size * 8000000ll / mDurationUs; // in bits/sec - size_t cachedSize = mCachedSource->cachedSize(); - int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate; + size_t cachedSize = mCachedSource->cachedSize(); + int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate; - double percentage = (double)cachedDurationUs / mDurationUs; + int percentage = 100.0 * (double)cachedDurationUs / mDurationUs; + if (percentage > 100) { + percentage = 100; + } - notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage * 100.0); + notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage); - lowWatermark = 2 * bitrate / 8; // 2 secs - highWatermark = 10 * bitrate / 8; // 10 secs + lowWatermark = 2 * bitrate / 8; // 2 secs + highWatermark = 10 * bitrate / 8; // 10 secs + } } - bool eos; - size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos); - if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) { LOGI("cache is running low (< %d) , pausing.", lowWatermark); mFlags |= CACHE_UNDERRUN; diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 90a596c..49eac62 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -25,6 +25,7 @@ #include "matroska/MatroskaExtractor.h" +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/FileSource.h> #include <media/stagefright/MediaErrors.h> @@ -56,19 +57,23 @@ status_t DataSource::getSize(off_t *size) { Mutex DataSource::gSnifferMutex; List<DataSource::SnifferFunc> DataSource::gSniffers; -bool DataSource::sniff(String8 *mimeType, float *confidence) { +bool DataSource::sniff( + String8 *mimeType, float *confidence, sp<AMessage> *meta) { *mimeType = ""; *confidence = 0.0f; + meta->clear(); Mutex::Autolock autoLock(gSnifferMutex); for (List<SnifferFunc>::iterator it = gSniffers.begin(); it != gSniffers.end(); ++it) { String8 newMimeType; float newConfidence; - if ((*it)(this, &newMimeType, &newConfidence)) { + sp<AMessage> newMeta; + if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) { if (newConfidence > *confidence) { *mimeType = newMimeType; *confidence = newConfidence; + *meta = newMeta; } } } @@ -92,13 +97,13 @@ void DataSource::RegisterSniffer(SnifferFunc func) { // static void DataSource::RegisterDefaultSniffers() { - RegisterSniffer(SniffMP3); RegisterSniffer(SniffMPEG4); - RegisterSniffer(SniffAMR); - RegisterSniffer(SniffWAV); - RegisterSniffer(SniffOgg); RegisterSniffer(SniffMatroska); + RegisterSniffer(SniffOgg); + RegisterSniffer(SniffWAV); + RegisterSniffer(SniffAMR); RegisterSniffer(SniffMPEG2TS); + RegisterSniffer(SniffMP3); } // static diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp index 4058fbc..2e36968 100644 --- a/media/libstagefright/MP3Extractor.cpp +++ b/media/libstagefright/MP3Extractor.cpp @@ -22,6 +22,7 @@ #include "include/ID3.h" +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> @@ -456,15 +457,31 @@ private: MP3Source &operator=(const MP3Source &); }; -MP3Extractor::MP3Extractor(const sp<DataSource> &source) +MP3Extractor::MP3Extractor( + const sp<DataSource> &source, const sp<AMessage> &meta) : mDataSource(source), mFirstFramePos(-1), mFixedHeader(0), mByteNumber(0) { off_t pos = 0; uint32_t header; - bool success = Resync(mDataSource, 0, &pos, &header); - CHECK(success); + bool success; + + int64_t meta_offset; + uint32_t meta_header; + if (meta != NULL + && meta->findInt64("offset", &meta_offset) + && meta->findInt32("header", (int32_t *)&meta_header)) { + // The sniffer has already done all the hard work for us, simply + // accept its judgement. + pos = (off_t)meta_offset; + header = meta_header; + + success = true; + } else { + success = Resync(mDataSource, 0, &pos, &header); + CHECK(success); + } if (success) { mFirstFramePos = pos; @@ -759,15 +776,20 @@ sp<MetaData> MP3Extractor::getMetaData() { } bool SniffMP3( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, + float *confidence, sp<AMessage> *meta) { off_t pos = 0; uint32_t header; if (!Resync(source, 0, &pos, &header)) { return false; } + *meta = new AMessage; + (*meta)->setInt64("offset", pos); + (*meta)->setInt32("header", header); + *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG; - *confidence = 0.3f; + *confidence = 0.2f; return true; } diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 12a1e6e..ba90407 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -1738,7 +1738,7 @@ static bool LegacySniffMPEG4( || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8) || !memcmp(header, "ftypkddi", 8) || !memcmp(header, "ftypM4VP", 8)) { *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; - *confidence = 0.1; + *confidence = 0.4; return true; } @@ -1805,13 +1805,14 @@ static bool BetterSniffMPEG4( } *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; - *confidence = 0.3f; + *confidence = 0.4f; return true; } bool SniffMPEG4( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { if (BetterSniffMPEG4(source, mimeType, confidence)) { return true; } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index e36d9fe..a15b84e 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -1569,9 +1569,9 @@ status_t MPEG4Writer::Track::threadEntry() { // The idea here is to avoid having two or more samples with the // same timestamp in the output file. if (mTimeScale >= 1000000LL) { - timestampUs += 1; + timestampUs = lastTimestampUs + 1; } else { - timestampUs += (1000000LL + (mTimeScale >> 1)) / mTimeScale; + timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale; } #endif } diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 56e6136..9bc94de 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -27,6 +27,7 @@ #include "matroska/MatroskaExtractor.h" +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaExtractor.h> @@ -46,10 +47,12 @@ uint32_t MediaExtractor::flags() const { // static sp<MediaExtractor> MediaExtractor::Create( const sp<DataSource> &source, const char *mime) { + sp<AMessage> meta; + String8 tmp; if (mime == NULL) { float confidence; - if (!source->sniff(&tmp, &confidence)) { + if (!source->sniff(&tmp, &confidence, &meta)) { LOGV("FAILED to autodetect media content."); return NULL; @@ -64,7 +67,7 @@ sp<MediaExtractor> MediaExtractor::Create( || !strcasecmp(mime, "audio/mp4")) { return new MPEG4Extractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) { - return new MP3Extractor(source); + return new MP3Extractor(source, meta); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { return new AMRExtractor(source); diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp index 9630092..2c1311a 100644 --- a/media/libstagefright/OggExtractor.cpp +++ b/media/libstagefright/OggExtractor.cpp @@ -804,7 +804,8 @@ sp<MetaData> OggExtractor::getMetaData() { } bool SniffOgg( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { char tmp[4]; if (source->readAt(0, tmp, 4) < 4 || memcmp(tmp, "OggS", 4)) { return false; diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 8d820c0..57c1075 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -404,7 +404,8 @@ status_t WAVSource::read( //////////////////////////////////////////////////////////////////////////////// bool SniffWAV( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { char header[12]; if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { return false; diff --git a/media/libstagefright/id3/ID3.cpp b/media/libstagefright/id3/ID3.cpp index ca0c68c..da340f7 100644 --- a/media/libstagefright/id3/ID3.cpp +++ b/media/libstagefright/id3/ID3.cpp @@ -142,11 +142,22 @@ struct id3_header { mSize = size; if (source->readAt(sizeof(header), mData, mSize) != (ssize_t)mSize) { + free(mData); + mData = NULL; + return false; } - if (header.flags & 0x80) { + if (header.version_major == 4) { + if (!removeUnsynchronizationV2_4()) { + free(mData); + mData = NULL; + + return false; + } + } else if (header.flags & 0x80) { LOGV("removing unsynchronization"); + removeUnsynchronization(); } @@ -243,6 +254,74 @@ void ID3::removeUnsynchronization() { } } +static void WriteSyncsafeInteger(uint8_t *dst, size_t x) { + for (size_t i = 0; i < 4; ++i) { + dst[3 - i] = (x & 0x7f); + x >>= 7; + } +} + +bool ID3::removeUnsynchronizationV2_4() { + size_t oldSize = mSize; + + size_t offset = 0; + while (offset + 10 <= mSize) { + if (!memcmp(&mData[offset], "\0\0\0\0", 4)) { + break; + } + + size_t dataSize; + if (!ParseSyncsafeInteger(&mData[offset + 4], &dataSize)) { + return false; + } + + if (offset + dataSize + 10 > mSize) { + return false; + } + + uint16_t flags = U16_AT(&mData[offset + 8]); + uint16_t prevFlags = flags; + + if (flags & 1) { + // Strip data length indicator + + memmove(&mData[offset + 10], &mData[offset + 14], mSize - offset - 14); + mSize -= 4; + dataSize -= 4; + + flags &= ~1; + } + + if (flags & 2) { + // Unsynchronization added. + + for (size_t i = 0; i + 1 < dataSize; ++i) { + if (mData[offset + 10 + i] == 0xff + && mData[offset + 11 + i] == 0x00) { + memmove(&mData[offset + 11 + i], &mData[offset + 12 + i], + mSize - offset - 12 - i); + --mSize; + --dataSize; + } + } + + flags &= ~2; + } + + if (flags != prevFlags) { + WriteSyncsafeInteger(&mData[offset + 4], dataSize); + mData[offset + 8] = flags >> 8; + mData[offset + 9] = flags & 0xff; + } + + offset += 10 + dataSize; + } + + memset(&mData[mSize], 0, oldSize - mSize); + + return true; +} + ID3::Iterator::Iterator(const ID3 &parent, const char *id) : mParent(parent), mID(NULL), @@ -529,10 +608,11 @@ void ID3::Iterator::findFrame() { uint16_t flags = U16_AT(&mParent.mData[mOffset + 8]); - if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000e)) + if ((mParent.mVersion == ID3_V2_4 && (flags & 0x000c)) || (mParent.mVersion == ID3_V2_3 && (flags & 0x00c0))) { - // Compression, Encryption or per-Frame unsynchronization - // are not supported at this time. + // Compression or encryption are not supported at this time. + // Per-frame unsynchronization and data-length indicator + // have already been taken care of. LOGV("Skipping unsupported frame (compression, encryption " "or per-frame unsynchronization flagged"); diff --git a/media/libstagefright/include/AMRExtractor.h b/media/libstagefright/include/AMRExtractor.h index db49fe4..1cdf36d 100644 --- a/media/libstagefright/include/AMRExtractor.h +++ b/media/libstagefright/include/AMRExtractor.h @@ -22,6 +22,7 @@ namespace android { +struct AMessage; class String8; class AMRExtractor : public MediaExtractor { @@ -49,7 +50,8 @@ private: }; bool SniffAMR( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); } // namespace android diff --git a/media/libstagefright/include/ID3.h b/media/libstagefright/include/ID3.h index c6b1a8b..7ddbb41 100644 --- a/media/libstagefright/include/ID3.h +++ b/media/libstagefright/include/ID3.h @@ -80,6 +80,7 @@ private: bool parseV1(const sp<DataSource> &source); bool parseV2(const sp<DataSource> &source); void removeUnsynchronization(); + bool removeUnsynchronizationV2_4(); static bool ParseSyncsafeInteger(const uint8_t encoded[4], size_t *x); diff --git a/media/libstagefright/include/MP3Extractor.h b/media/libstagefright/include/MP3Extractor.h index 3ce6df3..0e6ccde 100644 --- a/media/libstagefright/include/MP3Extractor.h +++ b/media/libstagefright/include/MP3Extractor.h @@ -22,13 +22,14 @@ namespace android { +struct AMessage; class DataSource; class String8; class MP3Extractor : public MediaExtractor { public: // Extractor assumes ownership of "source". - MP3Extractor(const sp<DataSource> &source); + MP3Extractor(const sp<DataSource> &source, const sp<AMessage> &meta); virtual size_t countTracks(); virtual sp<MediaSource> getTrack(size_t index); @@ -52,7 +53,8 @@ private: }; bool SniffMP3( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *meta); } // namespace android diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h index c96973b..1bf4cd1 100644 --- a/media/libstagefright/include/MPEG2TSExtractor.h +++ b/media/libstagefright/include/MPEG2TSExtractor.h @@ -9,6 +9,7 @@ namespace android { +struct AMessage; struct AnotherPacketSource; struct ATSParser; struct DataSource; @@ -47,7 +48,8 @@ private: }; bool SniffMPEG2TS( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); } // namespace android diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index c8663d5..1c9cc7e 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -23,6 +23,7 @@ namespace android { +struct AMessage; class DataSource; class SampleTable; class String8; @@ -75,7 +76,8 @@ private: }; bool SniffMPEG4( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); } // namespace android diff --git a/media/libstagefright/include/OggExtractor.h b/media/libstagefright/include/OggExtractor.h index 7066669..1eda025 100644 --- a/media/libstagefright/include/OggExtractor.h +++ b/media/libstagefright/include/OggExtractor.h @@ -22,6 +22,7 @@ namespace android { +struct AMessage; class DataSource; class String8; @@ -53,7 +54,8 @@ private: }; bool SniffOgg( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); } // namespace android diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h index 3e847b9..df6d3e7 100644 --- a/media/libstagefright/include/WAVExtractor.h +++ b/media/libstagefright/include/WAVExtractor.h @@ -22,6 +22,7 @@ namespace android { +struct AMessage; class DataSource; class String8; @@ -58,7 +59,8 @@ private: }; bool SniffWAV( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); } // namespace android diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index 71f6587..7c7d69e 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -579,7 +579,8 @@ sp<MetaData> MatroskaExtractor::getMetaData() { } bool SniffMatroska( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { DataSourceReader reader(source); mkvparser::EBMLHeader ebmlHeader; long long pos; diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h index 7471848..fa20b84 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.h +++ b/media/libstagefright/matroska/MatroskaExtractor.h @@ -27,6 +27,7 @@ struct Segment; namespace android { +struct AMessage; class String8; struct DataSourceReader; @@ -69,7 +70,8 @@ private: }; bool SniffMatroska( - const sp<DataSource> &source, String8 *mimeType, float *confidence); + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); } // namespace android diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index b287c95..56ca375 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -174,7 +174,8 @@ status_t MPEG2TSExtractor::feedMore() { //////////////////////////////////////////////////////////////////////////////// bool SniffMPEG2TS( - const sp<DataSource> &source, String8 *mimeType, float *confidence) { + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { #if 0 char header; if (source->readAt(0, &header, 1) != 1 || header != 0x47) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java index c7f461e..46135ff 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java @@ -27,7 +27,9 @@ import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest; import com.android.mediaframeworktest.functional.MediaAudioManagerTest; import com.android.mediaframeworktest.functional.MediaAudioEffectTest; import com.android.mediaframeworktest.functional.MediaBassBoostTest; +import com.android.mediaframeworktest.functional.MediaEnvReverbTest; import com.android.mediaframeworktest.functional.MediaEqualizerTest; +import com.android.mediaframeworktest.functional.MediaPresetReverbTest; import com.android.mediaframeworktest.functional.MediaVirtualizerTest; import com.android.mediaframeworktest.functional.MediaVisualizerTest; import junit.framework.TestSuite; @@ -62,7 +64,9 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { suite.addTestSuite(MediaAudioManagerTest.class); suite.addTestSuite(MediaAudioEffectTest.class); suite.addTestSuite(MediaBassBoostTest.class); + suite.addTestSuite(MediaEnvReverbTest.class); suite.addTestSuite(MediaEqualizerTest.class); + suite.addTestSuite(MediaPresetReverbTest.class); suite.addTestSuite(MediaVirtualizerTest.class); suite.addTestSuite(MediaVisualizerTest.class); return suite; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java new file mode 100644 index 0000000..d339e06 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import android.media.Visualizer; +import android.util.Log; + +/** + * The EnergyProbe class provides audio signal energy measurements based on the FFT returned + * by the Visualizer class. The measure is qualitative and not quantitative in that the returned + * value has no unit and is just proportional to the amount of energy present around the + * specified frequency. + */ + +public class EnergyProbe { + private String TAG = "EnergyProbe"; + + private static int CAPTURE_SIZE = 1024; + private static int MEASURE_COUNT = 5; + private static int AVERAGE_COUNT = 3; + + private Visualizer mVisualizer = null; + private int mMaxFrequency = 0; + private int mCapturePeriodMs; + private byte[] mFft = new byte[CAPTURE_SIZE]; + + public EnergyProbe(int session) { + try { + mVisualizer = new Visualizer(session); + if (mVisualizer != null) { + mVisualizer.setCaptureSize(CAPTURE_SIZE); + mMaxFrequency = mVisualizer.getSamplingRate() / 2000; + mCapturePeriodMs = 1000000 / mVisualizer.getMaxCaptureRate(); + } + } catch (UnsupportedOperationException e) { + Log.e(TAG, "Error creating visualizer"); + } catch (IllegalStateException e) { + Log.e(TAG, "Error configuring visualizer"); + } + } + + public int capture(int freq) throws InterruptedException { + int energy = 0; + int count = 0; + + if (freq > mMaxFrequency) { + return 0; + } + + if (mVisualizer != null) { + try { + mVisualizer.setEnabled(true); + for (int i = 0; i < MEASURE_COUNT; i++) { + if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { + if (freq == mMaxFrequency) { + energy += (int)mFft[0] * (int)mFft[0]; + } else { + int bin = 2 * (freq * CAPTURE_SIZE / mMaxFrequency / 2); + if (bin < 2) bin = 2; + int tmp = 0; + int j; + for (j = 0; + (j < AVERAGE_COUNT) && ((bin + 2 * j) < CAPTURE_SIZE); + j++) { + tmp += (int)mFft[bin + 2 * j] * (int)mFft[bin + 2 * j] + + (int)mFft[bin + 2 * j + 1] * (int)mFft[bin + 2 * j + 1]; + } + // j is always != 0 + energy += tmp/j; + } + count++; + } + Thread.sleep(mCapturePeriodMs); + } + mVisualizer.setEnabled(false); + } catch (IllegalStateException e) { + Log.e(TAG, "Error capturing audio"); + } + } + if (count == 0) { + return 0; + } + return energy/count; + } + + public void release() { + if (mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java index 8a68c5e..aca729e 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java @@ -43,7 +43,7 @@ import java.util.UUID; */ public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { private String TAG = "MediaBassBoostTest"; - private final static int MIN_ENERGY_RATIO_2 = 4; + private final static int MIN_ENERGY_RATIO_2 = 3; private final static short TEST_STRENGTH = 500; private BassBoost mBassBoost = null; @@ -259,52 +259,6 @@ public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFr // private methods //---------------------------------- - private class EnergyProbe { - Visualizer mVisualizer = null; - private byte[] mFft = new byte[1024]; - - public EnergyProbe(int session) { - mVisualizer = new Visualizer(session); - mVisualizer.setCaptureSize(1024); - } - - public int capture(int freq) throws InterruptedException { - int energy = 0; - int count = 0; - if (mVisualizer != null) { - mVisualizer.setEnabled(true); - for (int i = 0; i < 10; i++) { - if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { - // TODO: check speex FFT as it seems to return only the number of points - // correspondong to valid part of the spectrum (< Fs). - // e.g., if the number of points is 1024, it covers the frequency range - // 0 to 22050 instead of 0 to 44100 as expected from an FFT. - int bin = freq / (22050 / 1024); - int tmp = 0; - for (int j = bin-2; j < bin+3; j++) { - tmp += (int)mFft[j] * (int)mFft[j]; - } - energy += tmp/5; - count++; - } - Thread.sleep(50); - } - mVisualizer.setEnabled(false); - } - if (count == 0) { - return 0; - } - return energy/count; - } - - public void release() { - if (mVisualizer != null) { - mVisualizer.release(); - mVisualizer = null; - } - } - } - private void getBassBoost(int session) { if (mBassBoost == null || session != mSession) { if (session != mSession && mBassBoost != null) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java new file mode 100644 index 0000000..db0db70 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.EnvironmentalReverb; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaEnvReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaEnvReverbTest"; + // allow +/- 100 millibel difference between set and get gains + private final static int MILLIBEL_TOLERANCE = 100; + // allow +/- 5% tolerance between set and get delays + private final static float DELAY_TOLERANCE = 1.05f; + // allow +/- 5% tolerance between set and get ratios + private final static float RATIO_TOLERANCE = 1.05f; + + private EnvironmentalReverb mReverb = null; + private int mSession = -1; + + public MediaEnvReverbTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseReverb(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // ENVIRONMENTAL REVEB TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + EnvironmentalReverb reverb = null; + try { + reverb = new EnvironmentalReverb(0, 0); + assertNotNull(msg + ": could not create EnvironmentalReverb", reverb); + try { + assertTrue(msg +": invalid effect ID", (reverb.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": EnvironmentalReverb not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": EnvironmentalReverb not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (reverb != null) { + reverb.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test room level and room HF level + @LargeTest + public void test1_0Room() throws Exception { + boolean result = false; + String msg = "test1_0Room()"; + getReverb(0); + try { + mReverb.setRoomLevel((short)0); + short level = mReverb.getRoomLevel(); + assertTrue(msg +": got incorrect room level", + (level > (0 - MILLIBEL_TOLERANCE)) && + (level < (0 + MILLIBEL_TOLERANCE))); + + mReverb.setRoomHFLevel((short)-6); + level = mReverb.getRoomHFLevel(); + assertTrue(msg +": got incorrect room HF level", + (level > (-6 - MILLIBEL_TOLERANCE)) && + (level < (-6 + MILLIBEL_TOLERANCE))); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test decay time and ratio + @LargeTest + public void test1_1Decay() throws Exception { + boolean result = false; + String msg = "test1_1Decay()"; + getReverb(0); + try { + mReverb.setDecayTime(500); + int time = mReverb.getDecayTime(); + assertTrue(msg +": got incorrect decay time", + ((float)time > (float)(500 / DELAY_TOLERANCE)) && + ((float)time < (float)(500 * DELAY_TOLERANCE))); + + mReverb.setDecayHFRatio((short)1000); + short ratio = mReverb.getDecayHFRatio(); + assertTrue(msg +": got incorrect decay HF ratio", + ((float)ratio > (float)(1000 / RATIO_TOLERANCE)) && + ((float)ratio < (float)(1000 * RATIO_TOLERANCE))); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.2: test reflections + @LargeTest + public void test1_2Reflections() throws Exception { + // TODO: uncomment when early reflections are implemented +// boolean result = false; +// String msg = "test1_2Reflections()"; +// getReverb(0); +// try { +// mReverb.setReflectionsLevel((short)0); +// short level = mReverb.getReflectionsLevel(); +// assertTrue(msg +": got incorrect reflections level", +// (level > (0 - MILLIBEL_TOLERANCE)) && +// (level < (0 + MILLIBEL_TOLERANCE))); +// +// mReverb.setReflectionsDelay(30); +// int delay = mReverb.getReflectionsDelay(); +// assertTrue(msg +": got incorrect reflections delay", +// ((float)delay > (float)(30 / DELAY_TOLERANCE)) && +// ((float)delay < (float)(30 * DELAY_TOLERANCE))); +// +// result = true; +// } catch (IllegalArgumentException e) { +// msg = msg.concat(": Bad parameter value"); +// loge(msg, "Bad parameter value"); +// } catch (UnsupportedOperationException e) { +// msg = msg.concat(": get parameter() rejected"); +// loge(msg, "get parameter() rejected"); +// } catch (IllegalStateException e) { +// msg = msg.concat("get parameter() called in wrong state"); +// loge(msg, "get parameter() called in wrong state"); +// } finally { +// releaseReverb(); +// } +// assertTrue(msg, result); + } + + //Test case 1.3: test reverb + @LargeTest + public void test1_3Reverb() throws Exception { + boolean result = false; + String msg = "test1_3Reverb()"; + getReverb(0); + try { + mReverb.setReverbLevel((short)0); + short level = mReverb.getReverbLevel(); + assertTrue(msg +": got incorrect reverb level", + (level > (0 - MILLIBEL_TOLERANCE)) && + (level < (0 + MILLIBEL_TOLERANCE))); + + // TODO: change delay when early reflections are implemented + mReverb.setReverbDelay(0); + int delay = mReverb.getReverbDelay(); + assertTrue(msg +": got incorrect reverb delay", delay < 5); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.4: test diffusion and density + @LargeTest + public void test1_4DiffusionAndDensity() throws Exception { + boolean result = false; + String msg = "test1_4DiffusionAndDensity()"; + getReverb(0); + try { + mReverb.setDiffusion((short)500); + short diffusion = mReverb.getDiffusion(); + assertTrue(msg +": got incorrect diffusion", + ((float)diffusion > (float)(500 / RATIO_TOLERANCE)) && + ((float)diffusion < (float)(500 * RATIO_TOLERANCE))); + + mReverb.setDensity((short)500); + short density = mReverb.getDensity(); + assertTrue(msg +": got incorrect density", + ((float)density > (float)(500 / RATIO_TOLERANCE)) && + ((float)density < (float)(500 * RATIO_TOLERANCE))); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.5: test properties + @LargeTest + public void test1_5Properties() throws Exception { + boolean result = false; + String msg = "test1_5Properties()"; + getReverb(0); + try { + EnvironmentalReverb.Settings settings = mReverb.getProperties(); + short newRoomLevel = 0; + if (settings.roomLevel == 0) { + newRoomLevel = -1000; + } + String str = settings.toString(); + settings = new EnvironmentalReverb.Settings(str); + settings.roomLevel = newRoomLevel; + mReverb.setProperties(settings); + settings = mReverb.getProperties(); + assertTrue(msg +": setProperties failed", + (settings.roomLevel > (newRoomLevel - MILLIBEL_TOLERANCE)) && + (settings.roomLevel < (newRoomLevel + MILLIBEL_TOLERANCE))); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test actual auxiliary reverb influence on sound + @LargeTest + public void test2_0AuxiliarySoundModification() throws Exception { + boolean result = false; + String msg = "test2_0AuxiliarySoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + getReverb(0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + mp.attachAuxEffect(mReverb.getId()); + mp.setAuxEffectSendLevel(1.0f); + mReverb.setRoomLevel((short)0); + mReverb.setReverbLevel((short)0); + mReverb.setDecayTime(2000); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(300); + // measure energy around 1kHz after media player was stopped for 300 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //Test case 2.1: test actual insert reverb influence on sound + @LargeTest + public void test2_1InsertSoundModification() throws Exception { + boolean result = false; + String msg = "test2_1InsertSoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getReverb(mp.getAudioSessionId()); + mReverb.setRoomLevel((short)0); + mReverb.setReverbLevel((short)0); + mReverb.setDecayTime(2000); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(300); + // measure energy around 1kHz after media player was stopped for 300 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private void getReverb(int session) { + if (mReverb == null || session != mSession) { + if (session != mSession && mReverb != null) { + mReverb.release(); + mReverb = null; + } + try { + mReverb = new EnvironmentalReverb(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getReverb() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mReverb", mReverb); + } + + private void releaseReverb() { + if (mReverb != null) { + mReverb.release(); + mReverb = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java index e46887b..7b3945d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java @@ -322,52 +322,6 @@ public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFr // private methods //---------------------------------- - private class EnergyProbe { - Visualizer mVisualizer = null; - private byte[] mFft = new byte[1024]; - - public EnergyProbe(int session) { - mVisualizer = new Visualizer(session); - mVisualizer.setCaptureSize(1024); - } - - public int capture(int freq) throws InterruptedException { - int energy = 0; - int count = 0; - if (mVisualizer != null) { - mVisualizer.setEnabled(true); - for (int i = 0; i < 10; i++) { - if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { - // TODO: check speex FFT as it seems to return only the number of points - // correspondong to valid part of the spectrum (< Fs). - // e.g., if the number of points is 1024, it covers the frequency range - // 0 to 22050 instead of 0 to 44100 as expected from an FFT. - int bin = freq / (22050 / 1024); - int tmp = 0; - for (int j = bin-2; j < bin+3; j++) { - tmp += (int)mFft[j] * (int)mFft[j]; - } - energy += tmp/5; - count++; - } - Thread.sleep(50); - } - mVisualizer.setEnabled(false); - } - if (count == 0) { - return 0; - } - return energy/count; - } - - public void release() { - if (mVisualizer != null) { - mVisualizer.release(); - mVisualizer = null; - } - } - } - private void getEqualizer(int session) { if (mEqualizer == null || session != mSession) { if (session != mSession && mEqualizer != null) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java new file mode 100644 index 0000000..c14319a --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.PresetReverb; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaPresetReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaPresetReverbTest"; + + private PresetReverb mReverb = null; + private int mSession = -1; + + public MediaPresetReverbTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseReverb(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // PRESET REVEB TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + PresetReverb reverb = null; + try { + reverb = new PresetReverb(0, 0); + assertNotNull(msg + ": could not create PresetReverb", reverb); + try { + assertTrue(msg +": invalid effect ID", (reverb.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": PresetReverb not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": PresetReverb not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (reverb != null) { + reverb.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test preset + @LargeTest + public void test1_0Preset() throws Exception { + boolean result = false; + String msg = "test1_0Preset()"; + getReverb(0); + try { + mReverb.setPreset((short)PresetReverb.PRESET_LARGEROOM); + short preset = mReverb.getPreset(); + assertEquals(msg +": got incorrect preset", + (short)PresetReverb.PRESET_LARGEROOM, + preset); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test properties + @LargeTest + public void test1_1Properties() throws Exception { + boolean result = false; + String msg = "test1_1Properties()"; + getReverb(0); + try { + PresetReverb.Settings settings = mReverb.getProperties(); + short newPreset = (short)PresetReverb.PRESET_LARGEROOM; + if (settings.preset == (short)PresetReverb.PRESET_LARGEROOM) { + newPreset = (short)PresetReverb.PRESET_SMALLROOM; + } + String str = settings.toString(); + settings = new PresetReverb.Settings(str); + settings.preset = newPreset; + mReverb.setProperties(settings); + settings = mReverb.getProperties(); + assertEquals(msg +": setProperties failed", newPreset, settings.preset); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test actual auxiliary reverb influence on sound + @LargeTest + public void test2_0AuxiliarySoundModification() throws Exception { + boolean result = false; + String msg = "test2_0AuxiliarySoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + getReverb(0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + mp.attachAuxEffect(mReverb.getId()); + mp.setAuxEffectSendLevel(1.0f); + mReverb.setPreset((short)PresetReverb.PRESET_PLATE); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(200); + // measure energy around 1kHz after media player was stopped for 200 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //Test case 2.1: test actual insert reverb influence on sound + @LargeTest + public void test2_1InsertSoundModification() throws Exception { + boolean result = false; + String msg = "test2_1InsertSoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getReverb(mp.getAudioSessionId()); + mReverb.setPreset((short)PresetReverb.PRESET_PLATE); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(200); + // measure energy around 1kHz after media player was stopped for 200 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private void getReverb(int session) { + if (mReverb == null || session != mSession) { + if (session != mSession && mReverb != null) { + mReverb.release(); + mReverb = null; + } + try { + mReverb = new PresetReverb(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getReverb() PresetReverb not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getReverb() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mReverb", mReverb); + } + + private void releaseReverb() { + if (mReverb != null) { + mReverb.release(); + mReverb = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java index 6b8ae44..517d575 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java @@ -43,7 +43,7 @@ import java.util.UUID; */ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { private String TAG = "MediaVirtualizerTest"; - private final static int MIN_ENERGY_RATIO_2 = 4; + private final static int MIN_ENERGY_RATIO_2 = 3; private final static short TEST_STRENGTH = 500; private Virtualizer mVirtualizer = null; @@ -224,7 +224,7 @@ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<Media int energy200 = probe.capture(200); int energy1000 = probe.capture(1000); // verify that the energy ration between low and high frequencies is at least - // four times higher with virtualizer on. + // MIN_ENERGY_RATIO_2 times higher with virtualizer on. // NOTE: this is what is observed with current virtualizer implementation and the test // audio file but is not the primary effect of the virtualizer. A better way would // be to have a stereo PCM capture and check that a strongly paned input is centered @@ -264,52 +264,6 @@ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<Media // private methods //---------------------------------- - private class EnergyProbe { - Visualizer mVisualizer = null; - private byte[] mFft = new byte[1024]; - - public EnergyProbe(int session) { - mVisualizer = new Visualizer(session); - mVisualizer.setCaptureSize(1024); - } - - public int capture(int freq) throws InterruptedException { - int energy = 0; - int count = 0; - if (mVisualizer != null) { - mVisualizer.setEnabled(true); - for (int i = 0; i < 10; i++) { - if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { - // TODO: check speex FFT as it seems to return only the number of points - // correspondong to valid part of the spectrum (< Fs). - // e.g., if the number of points is 1024, it covers the frequency range - // 0 to 22050 instead of 0 to 44100 as expected from an FFT. - int bin = freq / (22050 / 1024); - int tmp = 0; - for (int j = bin-2; j < bin+3; j++) { - tmp += (int)mFft[j] * (int)mFft[j]; - } - energy += tmp/5; - count++; - } - Thread.sleep(50); - } - mVisualizer.setEnabled(false); - } - if (count == 0) { - return 0; - } - return energy/count; - } - - public void release() { - if (mVisualizer != null) { - mVisualizer.release(); - mVisualizer = null; - } - } - } - private void getVirtualizer(int session) { if (mVirtualizer == null || session != mSession) { if (session != mSession && mVirtualizer != null) { diff --git a/opengl/java/android/opengl/GLWallpaperService.java b/opengl/java/android/opengl/GLWallpaperService.java new file mode 100644 index 0000000..c954fed --- /dev/null +++ b/opengl/java/android/opengl/GLWallpaperService.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.opengl; + +/** + * reserve this namespace for future use + * (making sure external developers don't use it) + * @hide + */ +class GLWallpaperService { +} diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 185d72a..8349fe6 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -47,7 +47,8 @@ <bool name="def_networks_available_notification_on">true</bool> <bool name="def_backup_enabled">false</bool> - <string name="def_backup_transport" translatable="false"></string> + <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string> + <!-- Default value for whether or not to pulse the notification LED when there is a pending notification --> <bool name="def_notification_pulse">true</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 2e95932..81d82de 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -22,6 +22,8 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.LinkedHashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import android.app.backup.BackupManager; import android.content.ContentProvider; @@ -37,6 +39,7 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; +import android.os.FileObserver; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; import android.provider.DrmStore; @@ -56,9 +59,15 @@ public class SettingsProvider extends ContentProvider { // Cache for settings, access-ordered for acting as LRU. // Guarded by themselves. - private static final int MAX_CACHE_ENTRIES = 50; - private static final SettingsCache sSystemCache = new SettingsCache(); - private static final SettingsCache sSecureCache = new SettingsCache(); + private static final int MAX_CACHE_ENTRIES = 200; + private static final SettingsCache sSystemCache = new SettingsCache("system"); + private static final SettingsCache sSecureCache = new SettingsCache("secure"); + + // The count of how many known (handled by SettingsProvider) + // database mutations are currently being handled. Used by + // sFileObserver to not reload the database when it's ourselves + // modifying it. + private static final AtomicInteger sKnownMutationsInFlight = new AtomicInteger(0); // Over this size we don't reject loading or saving settings but // we do consider them broken/malicious and don't keep them in @@ -67,6 +76,10 @@ public class SettingsProvider extends ContentProvider { private static final Bundle NULL_SETTING = Bundle.forPair("value", null); + // Used as a sentinel value in an instance equality test when we + // want to cache the existence of a key, but not store its value. + private static final Bundle TOO_LARGE_TO_CACHE_MARKER = Bundle.forPair("_dummy", null); + protected DatabaseHelper mOpenHelper; private BackupManager mBackupManager; @@ -201,6 +214,43 @@ public class SettingsProvider extends ContentProvider { } } + // FileObserver for external modifications to the database file. + // Note that this is for platform developers only with + // userdebug/eng builds who should be able to tinker with the + // sqlite database out from under the SettingsProvider, which is + // normally the exclusive owner of the database. But we keep this + // enabled all the time to minimize development-vs-user + // differences in testing. + private static SettingsFileObserver sObserverInstance; + private class SettingsFileObserver extends FileObserver { + private final AtomicBoolean mIsDirty = new AtomicBoolean(false); + private final String mPath; + + public SettingsFileObserver(String path) { + super(path, FileObserver.CLOSE_WRITE | + FileObserver.CREATE | FileObserver.DELETE | + FileObserver.MOVED_TO | FileObserver.MODIFY); + mPath = path; + } + + public void onEvent(int event, String path) { + int modsInFlight = sKnownMutationsInFlight.get(); + if (modsInFlight > 0) { + // our own modification. + return; + } + Log.d(TAG, "external modification to " + mPath + "; event=" + event); + if (!mIsDirty.compareAndSet(false, true)) { + // already handled. (we get a few update events + // during an sqlite write) + return; + } + Log.d(TAG, "updating our caches for " + mPath); + fullyPopulateCaches(); + mIsDirty.set(false); + } + } + @Override public boolean onCreate() { mOpenHelper = new DatabaseHelper(getContext()); @@ -210,9 +260,65 @@ public class SettingsProvider extends ContentProvider { return false; } + // Watch for external modifications to the database file, + // keeping our cache in sync. + // It's kinda lame to call mOpenHelper.getReadableDatabase() + // during onCreate(), but since ensureAndroidIdIsSet has + // already done it above and initialized/upgraded the + // database, might as well just use it... + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + sObserverInstance = new SettingsFileObserver(db.getPath()); + sObserverInstance.startWatching(); + startAsyncCachePopulation(); return true; } + private void startAsyncCachePopulation() { + new Thread("populate-settings-caches") { + public void run() { + fullyPopulateCaches(); + } + }.start(); + } + + private void fullyPopulateCaches() { + fullyPopulateCache("secure", sSecureCache); + fullyPopulateCache("system", sSystemCache); + } + + // Slurp all values (if sane in number & size) into cache. + private void fullyPopulateCache(String table, SettingsCache cache) { + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + Cursor c = db.query( + table, + new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE }, + null, null, null, null, null, + "" + (MAX_CACHE_ENTRIES + 1) /* limit */); + try { + synchronized (cache) { + cache.clear(); + cache.setFullyMatchesDisk(true); // optimistic + int rows = 0; + while (c.moveToNext()) { + rows++; + String name = c.getString(0); + String value = c.getString(1); + cache.populate(name, value); + } + if (rows > MAX_CACHE_ENTRIES) { + // Somewhat redundant, as removeEldestEntry() will + // have already done this, but to be explicit: + cache.setFullyMatchesDisk(false); + Log.d(TAG, "row count exceeds max cache entries for table " + table); + } + Log.d(TAG, "cache for settings table '" + table + "' fullycached=" + + cache.fullyMatchesDisk()); + } + } finally { + c.close(); + } + } + private boolean ensureAndroidIdIsSet() { final Cursor c = query(Settings.Secure.CONTENT_URI, new String[] { Settings.NameValueTable.VALUE }, @@ -262,7 +368,19 @@ public class SettingsProvider extends ContentProvider { private Bundle lookupValue(String table, SettingsCache cache, String key) { synchronized (cache) { if (cache.containsKey(key)) { - return cache.get(key); + Bundle value = cache.get(key); + if (value != TOO_LARGE_TO_CACHE_MARKER) { + return value; + } + // else we fall through and read the value from disk + } else if (cache.fullyMatchesDisk()) { + // Fast path (very common). Don't even try touch disk + // if we know we've slurped it all in. Trying to + // touch the disk would mean waiting for yaffs2 to + // give us access, which could takes hundreds of + // milliseconds. And we're very likely being called + // from somebody's UI thread... + return NULL_SETTING; } } @@ -338,6 +456,7 @@ public class SettingsProvider extends ContentProvider { checkWritePermissions(args); SettingsCache cache = SettingsCache.forTable(args.table); + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.beginTransaction(); try { @@ -350,6 +469,7 @@ public class SettingsProvider extends ContentProvider { db.setTransactionSuccessful(); } finally { db.endTransaction(); + sKnownMutationsInFlight.decrementAndGet(); } sendNotify(uri); @@ -449,8 +569,10 @@ public class SettingsProvider extends ContentProvider { return Uri.withAppendedPath(url, name); } + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final long rowId = db.insert(args.table, null, initialValues); + sKnownMutationsInFlight.decrementAndGet(); if (rowId <= 0) return null; SettingsCache.populate(cache, initialValues); // before we notify @@ -471,12 +593,15 @@ public class SettingsProvider extends ContentProvider { } checkWritePermissions(args); + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.delete(args.table, args.where, args.args); + sKnownMutationsInFlight.decrementAndGet(); if (count > 0) { SettingsCache.wipe(args.table); // before we notify sendNotify(url); } + startAsyncCachePopulation(); if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) deleted"); return count; } @@ -489,12 +614,15 @@ public class SettingsProvider extends ContentProvider { } checkWritePermissions(args); + sKnownMutationsInFlight.incrementAndGet(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + sKnownMutationsInFlight.decrementAndGet(); int count = db.update(args.table, initialValues, args.where, args.args); if (count > 0) { SettingsCache.wipe(args.table); // before we notify sendNotify(url); } + startAsyncCachePopulation(); if (LOCAL_LOGV) Log.v(TAG, args.table + ": " + count + " row(s) <- " + initialValues); return count; } @@ -506,12 +634,12 @@ public class SettingsProvider extends ContentProvider { * When a client attempts to openFile the default ringtone or * notification setting Uri, we will proxy the call to the current * default ringtone's Uri (if it is in the DRM or media provider). - */ + */ int ringtoneType = RingtoneManager.getDefaultType(uri); // Above call returns -1 if the Uri doesn't match a default type if (ringtoneType != -1) { Context context = getContext(); - + // Get the current value for the default sound Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType); @@ -531,7 +659,7 @@ public class SettingsProvider extends ContentProvider { throw new FileNotFoundException(e.getMessage()); } } - + return context.getContentResolver().openFileDescriptor(soundUri, mode); } } @@ -607,13 +735,38 @@ public class SettingsProvider extends ContentProvider { */ private static final class SettingsCache extends LinkedHashMap<String, Bundle> { - public SettingsCache() { + private final String mCacheName; + private boolean mCacheFullyMatchesDisk = false; // has the whole database slurped. + + public SettingsCache(String name) { super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */); + mCacheName = name; + } + + /** + * Is the whole database table slurped into this cache? + */ + public boolean fullyMatchesDisk() { + synchronized (this) { + return mCacheFullyMatchesDisk; + } + } + + public void setFullyMatchesDisk(boolean value) { + synchronized (this) { + mCacheFullyMatchesDisk = value; + } } @Override protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_CACHE_ENTRIES; + if (size() <= MAX_CACHE_ENTRIES) { + return false; + } + synchronized (this) { + mCacheFullyMatchesDisk = false; + } + return true; } /** @@ -658,11 +811,15 @@ public class SettingsProvider extends ContentProvider { return; } String value = contentValues.getAsString(Settings.NameValueTable.VALUE); - synchronized (cache) { + cache.populate(name, value); + } + + public void populate(String name, String value) { + synchronized (this) { if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) { - cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value)); + put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value)); } else { - cache.remove(name); + put(name, TOO_LARGE_TO_CACHE_MARKER); } } } @@ -678,6 +835,7 @@ public class SettingsProvider extends ContentProvider { } synchronized (cache) { cache.clear(); + cache.mCacheFullyMatchesDisk = true; } } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 4d35bec..c61baad 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -77,9 +77,12 @@ import android.view.inputmethod.EditorInfo; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.text.Collator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.TreeMap; /** * This class provides a system service that manages input methods. @@ -463,14 +466,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt); + mStatusBar = statusBar; + statusBar.setIconVisibility("ime", false); + buildInputMethodListLocked(mMethodList, mMethodMap); final String enabledStr = Settings.Secure.getString( mContext.getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS); Slog.i(TAG, "Enabled input methods: " + enabledStr); - if (enabledStr == null) { - Slog.i(TAG, "Enabled input methods has not been set, enabling all"); + final String defaultIme = Settings.Secure.getString(mContext + .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); + if (enabledStr == null || TextUtils.isEmpty(defaultIme)) { + Slog.i(TAG, "Enabled input methods or default IME has not been set, enabling all"); InputMethodInfo defIm = null; StringBuilder sb = new StringBuilder(256); final int N = mMethodList.size(); @@ -504,9 +512,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - mStatusBar = statusBar; - statusBar.setIconVisibility("ime", false); - mSettingsObserver = new SettingsObserver(mHandler); updateFromSettingsLocked(); } @@ -978,7 +983,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void setInputMethodLocked(String id) { InputMethodInfo info = mMethodMap.get(id); if (info == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + throw new IllegalArgumentException("Unknown id: " + id); } if (id.equals(mCurMethodId)) { @@ -1476,7 +1481,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub String defaultIme = Settings.Secure.getString(mContext .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - if (!map.containsKey(defaultIme)) { + if (!TextUtils.isEmpty(defaultIme) && !map.containsKey(defaultIme)) { if (chooseNewDefaultIMELocked()) { updateFromSettingsLocked(); } @@ -1506,21 +1511,22 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideInputMethodMenuLocked(); int N = immis.size(); - - mItems = new CharSequence[N]; - mIms = new InputMethodInfo[N]; - - int j = 0; + + final Map<CharSequence, InputMethodInfo> imMap = + new TreeMap<CharSequence, InputMethodInfo>(Collator.getInstance()); + for (int i = 0; i < N; ++i) { InputMethodInfo property = immis.get(i); if (property == null) { continue; } - mItems[j] = property.loadLabel(pm); - mIms[j] = property; - j++; + imMap.put(property.loadLabel(pm), property); } - + + N = imMap.size(); + mItems = imMap.keySet().toArray(new CharSequence[N]); + mIms = imMap.values().toArray(new InputMethodInfo[N]); + int checkedItem = 0; for (int i = 0; i < N; ++i) { if (mIms[i].getId().equals(lastInputMethodId)) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 421d1c4..9b9d950 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -4808,6 +4808,8 @@ public class WindowManagerService extends IWindowManager.Stub Parcel data = null; Parcel reply = null; + BufferedWriter out = null; + // Any uncaught exception will crash the system process try { // Find the hashcode of the window @@ -4845,6 +4847,12 @@ public class WindowManagerService extends IWindowManager.Stub reply.readException(); + if (!client.isOutputShutdown()) { + out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); + out.write("DONE\n"); + out.flush(); + } + } catch (Exception e) { Slog.w(TAG, "Could not send command " + command + " with parameters " + parameters, e); success = false; @@ -4855,6 +4863,13 @@ public class WindowManagerService extends IWindowManager.Stub if (reply != null) { reply.recycle(); } + if (out != null) { + try { + out.close(); + } catch (IOException e) { + + } + } } return success; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index b37cd89..d535343 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -609,8 +609,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * All currently bound service connections. Keys are the IBinder of * the client's IServiceConnection. */ - final HashMap<IBinder, ConnectionRecord> mServiceConnections - = new HashMap<IBinder, ConnectionRecord>(); + final HashMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections + = new HashMap<IBinder, ArrayList<ConnectionRecord>>(); /** * List of services that we have been asked to start, @@ -4294,12 +4294,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " when granting permission to uri " + uri); } if (targetPkg == null) { - Slog.w(TAG, "grantUriPermission: null target"); - return; + throw new IllegalArgumentException("null target"); } if (uri == null) { - Slog.w(TAG, "grantUriPermission: null uri"); - return; + throw new IllegalArgumentException("null uri"); } grantUriPermissionLocked(r.info.uid, targetPkg, uri, modeFlags, @@ -4451,6 +4449,56 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + @Override + public IBinder newUriPermissionOwner(String name) { + synchronized(this) { + UriPermissionOwner owner = new UriPermissionOwner(this, name); + return owner.getExternalTokenLocked(); + } + } + + @Override + public void grantUriPermissionFromOwner(IBinder token, int fromUid, String targetPkg, + Uri uri, int modeFlags) { + synchronized(this) { + UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); + if (owner == null) { + throw new IllegalArgumentException("Unknown owner: " + token); + } + if (fromUid != Binder.getCallingUid()) { + if (Binder.getCallingUid() != Process.myUid()) { + // Only system code can grant URI permissions on behalf + // of other users. + throw new SecurityException("nice try"); + } + } + if (targetPkg == null) { + throw new IllegalArgumentException("null target"); + } + if (uri == null) { + throw new IllegalArgumentException("null uri"); + } + + grantUriPermissionLocked(fromUid, targetPkg, uri, modeFlags, owner); + } + } + + @Override + public void revokeUriPermissionFromOwner(IBinder token, Uri uri, int mode) { + synchronized(this) { + UriPermissionOwner owner = UriPermissionOwner.fromExternalToken(token); + if (owner == null) { + throw new IllegalArgumentException("Unknown owner: " + token); + } + + if (uri == null) { + owner.removeUriPermissionsLocked(mode); + } else { + owner.removeUriPermissionLocked(uri, mode); + } + } + } + public void showWaitingForDebugger(IApplicationThread who, boolean waiting) { synchronized (this) { ProcessRecord app = @@ -7376,12 +7424,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mServiceConnections.size() > 0) { if (needSep) pw.println(" "); pw.println(" Connection bindings to services:"); - Iterator<ConnectionRecord> it + Iterator<ArrayList<ConnectionRecord>> it = mServiceConnections.values().iterator(); while (it.hasNext()) { - ConnectionRecord r = it.next(); - pw.print(" * "); pw.println(r); - r.dump(pw, " "); + ArrayList<ConnectionRecord> r = it.next(); + for (int i=0; i<r.size(); i++) { + pw.print(" * "); pw.println(r.get(i)); + r.get(i).dump(pw, " "); + } } needSep = true; } @@ -7659,18 +7709,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (it.hasNext()) { ServiceRecord r = it.next(); if (r.connections.size() > 0) { - Iterator<ConnectionRecord> jt + Iterator<ArrayList<ConnectionRecord>> jt = r.connections.values().iterator(); while (jt.hasNext()) { - ConnectionRecord c = jt.next(); - if (c.binding.client != app) { - try { - //c.conn.connected(r.className, null); - } catch (Exception e) { - // todo: this should be asynchronous! - Slog.w(TAG, "Exception thrown disconnected servce " - + r.shortName - + " from app " + app.processName, e); + ArrayList<ConnectionRecord> cl = jt.next(); + for (int i=0; i<cl.size(); i++) { + ConnectionRecord c = cl.get(i); + if (c.binding.client != app) { + try { + //c.conn.connected(r.className, null); + } catch (Exception e) { + // todo: this should be asynchronous! + Slog.w(TAG, "Exception thrown disconnected servce " + + r.shortName + + " from app " + app.processName, e); + } } } } @@ -7700,7 +7753,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } sr.app = null; sr.executeNesting = 0; - mStoppingServices.remove(sr); + if (mStoppingServices.remove(sr)) { + if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); + } boolean hasClients = sr.bindings.size() > 0; if (hasClients) { @@ -7753,6 +7808,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ServiceRecord sr = mStoppingServices.get(i); if (sr.app == app) { mStoppingServices.remove(i); + if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr); } } @@ -8016,11 +8072,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.app != null && r.app.persistent) { info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS; } - for (ConnectionRecord conn : r.connections.values()) { - if (conn.clientLabel != 0) { - info.clientPackage = conn.binding.client.info.packageName; - info.clientLabel = conn.clientLabel; - break; + + for (ArrayList<ConnectionRecord> connl : r.connections.values()) { + for (int i=0; i<connl.size(); i++) { + ConnectionRecord conn = connl.get(i); + if (conn.clientLabel != 0) { + info.clientPackage = conn.binding.client.info.packageName; + info.clientLabel = conn.clientLabel; + return info; + } } } return info; @@ -8055,9 +8115,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { ServiceRecord r = mServices.get(name); if (r != null) { - for (ConnectionRecord conn : r.connections.values()) { - if (conn.clientIntent != null) { - return conn.clientIntent; + for (ArrayList<ConnectionRecord> conn : r.connections.values()) { + for (int i=0; i<conn.size(); i++) { + if (conn.get(i).clientIntent != null) { + return conn.get(i).clientIntent; + } } } } @@ -8234,8 +8296,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (r.pendingStarts.size() > 0) { try { ServiceRecord.StartItem si = r.pendingStarts.remove(0); - if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to service: " - + r.name + " " + r.intent + " args=" + si.intent); + if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: " + + r + " " + r.intent + " args=" + si.intent); if (si.intent == null) { // If somehow we got a dummy start at the front, then // just drop it here. @@ -8246,8 +8308,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen si.deliveryCount++; if (si.targetPermissionUid >= 0) { grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid, - r.packageName, si.intent, si); + r.packageName, si.intent, si.getUriPermissionsLocked()); } + if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r); bumpServiceExecutingLocked(r); if (!oomAdjusted) { oomAdjusted = true; @@ -8264,6 +8327,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { // Remote process gone... we'll let the normal cleanup take // care of this. + if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while scheduling start: " + r); break; } catch (Exception e) { Slog.w(TAG, "Unexpected exception", e); @@ -8280,9 +8344,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if ((!i.requested || rebind) && i.apps.size() > 0) { try { + if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r + + " in " + i + ": shouldUnbind=" + i.hasBound); bumpServiceExecutingLocked(r); - if (DEBUG_SERVICE) Slog.v(TAG, "Connecting binding " + i - + ": shouldUnbind=" + i.hasBound); r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind); if (!rebind) { i.requested = true; @@ -8290,6 +8354,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen i.hasBound = true; i.doRebind = false; } catch (RemoteException e) { + if (DEBUG_SERVICE) Slog.v(TAG, "Crashed while binding " + r); return false; } } @@ -8316,13 +8381,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); app.services.add(r); + if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent); bumpServiceExecutingLocked(r); updateLruProcessLocked(app, true, true); boolean created = false; try { - if (DEBUG_SERVICE) Slog.v(TAG, "Scheduling start service: " - + r.name + " " + r.intent); mStringBuilder.setLength(0); r.intent.getIntent().toShortString(mStringBuilder, false, true); EventLog.writeEvent(EventLogTags.AM_CREATE_SERVICE, @@ -8482,8 +8546,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return true; } - if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up service " + r.name - + " " + r.intent); + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent); // We are now bringing the service up, so no longer in the // restarting state. @@ -8534,27 +8597,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (!force) { // XXX should probably keep a count of the number of auto-create // connections directly in the service. - Iterator<ConnectionRecord> it = r.connections.values().iterator(); + Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); while (it.hasNext()) { - ConnectionRecord cr = it.next(); - if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { - return; + ArrayList<ConnectionRecord> cr = it.next(); + for (int i=0; i<cr.size(); i++) { + if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) { + return; + } } } } // Report to all of the connections that the service is no longer // available. - Iterator<ConnectionRecord> it = r.connections.values().iterator(); + Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); while (it.hasNext()) { - ConnectionRecord c = it.next(); - try { - // todo: shouldn't be a synchronous call! - c.conn.connected(r.name, null); - } catch (Exception e) { - Slog.w(TAG, "Failure disconnecting service " + r.name + - " to connection " + c.conn.asBinder() + - " (in " + c.binding.client.processName + ")", e); + ArrayList<ConnectionRecord> c = it.next(); + for (int i=0; i<c.size(); i++) { + try { + c.get(i).conn.connected(r.name, null); + } catch (Exception e) { + Slog.w(TAG, "Failure disconnecting service " + r.name + + " to connection " + c.get(i).conn.asBinder() + + " (in " + c.get(i).binding.client.processName + ")", e); + } } } } @@ -8568,6 +8634,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + ": hasBound=" + ibr.hasBound); if (r.app != null && r.app.thread != null && ibr.hasBound) { try { + if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r + + " for " + ibr); bumpServiceExecutingLocked(r); updateOomAdjLocked(r.app); ibr.hasBound = false; @@ -8582,15 +8650,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down service " + r.name - + " " + r.intent); + if (DEBUG_SERVICE) Slog.v(TAG, "Bringing down " + r + " " + r.intent); EventLog.writeEvent(EventLogTags.AM_DESTROY_SERVICE, System.identityHashCode(r), r.shortName, (r.app != null) ? r.app.pid : -1); mServices.remove(r.name); mServicesByIntent.remove(r.intent); - if (localLOGV) Slog.v(TAG, "BRING DOWN SERVICE: " + r.shortName); r.totalRestartCount = 0; unscheduleServiceRestartLocked(r); @@ -8599,8 +8665,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen for (int i=0; i<N; i++) { if (mPendingServices.get(i) == r) { mPendingServices.remove(i); - if (DEBUG_SERVICE) Slog.v( - TAG, "Removed pending service: " + r.shortName); + if (DEBUG_SERVICE) Slog.v(TAG, "Removed pending: " + r); i--; N--; } @@ -8622,8 +8687,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.services.remove(r); if (r.app.thread != null) { try { - if (DEBUG_SERVICE) Slog.v(TAG, - "Stopping service: " + r.shortName); + if (DEBUG_SERVICE) { + RuntimeException here = new RuntimeException(); + here.fillInStackTrace(); + Slog.v(TAG, ">>> EXECUTING stop of " + r, here); + } bumpServiceExecutingLocked(r); mStoppingServices.add(r); updateOomAdjLocked(r.app); @@ -8636,11 +8704,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen updateServiceForegroundLocked(r.app, false); } else { if (DEBUG_SERVICE) Slog.v( - TAG, "Removed service that has no process: " + r.shortName); + TAG, "Removed service that has no process: " + r); } } else { if (DEBUG_SERVICE) Slog.v( - TAG, "Removed service that is not running: " + r.shortName); + TAG, "Removed service that is not running: " + r); } } @@ -8675,8 +8743,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen int targetPermissionUid = checkGrantUriPermissionFromIntentLocked( callingUid, r.packageName, service); if (unscheduleServiceRestartLocked(r)) { - if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " - + r.shortName); + if (DEBUG_SERVICE) Slog.v(TAG, "START SERVICE WHILE RESTART PENDING: " + r); } r.startRequested = true; r.callStart = false; @@ -8970,7 +9037,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (unscheduleServiceRestartLocked(s)) { if (DEBUG_SERVICE) Slog.v(TAG, "BIND SERVICE WHILE RESTART PENDING: " - + s.shortName); + + s); } AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); @@ -8978,7 +9045,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen connection, flags, clientLabel, clientIntent); IBinder binder = connection.asBinder(); - s.connections.put(binder, c); + ArrayList<ConnectionRecord> clist = s.connections.get(binder); + if (clist == null) { + clist = new ArrayList<ConnectionRecord>(); + s.connections.put(binder, clist); + } + clist.add(c); b.connections.add(c); if (activity != null) { if (activity.connections == null) { @@ -8987,7 +9059,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activity.connections.add(c); } b.client.connections.add(c); - mServiceConnections.put(binder, c); + clist = mServiceConnections.get(binder); + if (clist == null) { + clist = new ArrayList<ConnectionRecord>(); + mServiceConnections.put(binder, clist); + } + clist.add(c); if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); @@ -9038,7 +9115,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen IBinder binder = c.conn.asBinder(); AppBindRecord b = c.binding; ServiceRecord s = b.service; - s.connections.remove(binder); + ArrayList<ConnectionRecord> clist = s.connections.get(binder); + if (clist != null) { + clist.remove(c); + if (clist.size() == 0) { + s.connections.remove(binder); + } + } b.connections.remove(c); if (c.activity != null && c.activity != skipAct) { if (c.activity.connections != null) { @@ -9048,7 +9131,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (b.client != skipApp) { b.client.connections.remove(c); } - mServiceConnections.remove(binder); + clist = mServiceConnections.get(binder); + if (clist != null) { + clist.remove(c); + if (clist.size() == 0) { + mServiceConnections.remove(binder); + } + } if (b.connections.size() == 0) { b.intent.apps.remove(b.client); @@ -9059,6 +9148,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 && b.intent.hasBound) { try { + if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s + + " from " + b); bumpServiceExecutingLocked(s); updateOomAdjLocked(s.app); b.intent.hasBound = false; @@ -9081,8 +9172,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (this) { IBinder binder = connection.asBinder(); if (DEBUG_SERVICE) Slog.v(TAG, "unbindService: conn=" + binder); - ConnectionRecord r = mServiceConnections.get(binder); - if (r == null) { + ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder); + if (clist == null) { Slog.w(TAG, "Unbind failed: could not find connection for " + connection.asBinder()); return false; @@ -9090,11 +9181,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); - removeConnectionLocked(r, null, null); + while (clist.size() > 0) { + ConnectionRecord r = clist.get(0); + removeConnectionLocked(r, null, null); - if (r.binding.service.app != null) { - // This could have made the service less important. - updateOomAdjLocked(r.binding.service.app); + if (r.binding.service.app != null) { + // This could have made the service less important. + updateOomAdjLocked(r.binding.service.app); + } } Binder.restoreCallingIdentity(origId); @@ -9117,7 +9211,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen final long origId = Binder.clearCallingIdentity(); - if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING SERVICE " + r.name + if (DEBUG_SERVICE) Slog.v(TAG, "PUBLISHING " + r + " " + intent + ": " + service); if (r != null) { Intent.FilterComparison filter @@ -9128,26 +9222,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen b.requested = true; b.received = true; if (r.connections.size() > 0) { - Iterator<ConnectionRecord> it + Iterator<ArrayList<ConnectionRecord>> it = r.connections.values().iterator(); while (it.hasNext()) { - ConnectionRecord c = it.next(); - if (!filter.equals(c.binding.intent.intent)) { - if (DEBUG_SERVICE) Slog.v( - TAG, "Not publishing to: " + c); - if (DEBUG_SERVICE) Slog.v( - TAG, "Bound intent: " + c.binding.intent.intent); - if (DEBUG_SERVICE) Slog.v( - TAG, "Published intent: " + intent); - continue; - } - if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c); - try { - c.conn.connected(r.name, service); - } catch (Exception e) { - Slog.w(TAG, "Failure sending service " + r.name + - " to connection " + c.conn.asBinder() + - " (in " + c.binding.client.processName + ")", e); + ArrayList<ConnectionRecord> clist = it.next(); + for (int i=0; i<clist.size(); i++) { + ConnectionRecord c = clist.get(i); + if (!filter.equals(c.binding.intent.intent)) { + if (DEBUG_SERVICE) Slog.v( + TAG, "Not publishing to: " + c); + if (DEBUG_SERVICE) Slog.v( + TAG, "Bound intent: " + c.binding.intent.intent); + if (DEBUG_SERVICE) Slog.v( + TAG, "Published intent: " + intent); + continue; + } + if (DEBUG_SERVICE) Slog.v(TAG, "Publishing to: " + c); + try { + c.conn.connected(r.name, service); + } catch (Exception e) { + Slog.w(TAG, "Failure sending service " + r.name + + " to connection " + c.conn.asBinder() + + " (in " + c.binding.client.processName + ")", e); + } } } } @@ -9208,9 +9305,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ServiceRecord r = (ServiceRecord)token; boolean inStopping = mStoppingServices.contains(token); if (r != null) { - if (DEBUG_SERVICE) Slog.v(TAG, "DONE EXECUTING SERVICE " + r.name - + ": nesting=" + r.executeNesting - + ", inStopping=" + inStopping); if (r != token) { Slog.w(TAG, "Done executing service " + r.name + " with incorrect token: given " + token @@ -9267,13 +9361,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen serviceDoneExecutingLocked(r, inStopping); Binder.restoreCallingIdentity(origId); } else { - Slog.w(TAG, "Done executing unknown service " + r.name - + " with token " + token); + Slog.w(TAG, "Done executing unknown service from pid " + + Binder.getCallingPid()); } } } public void serviceDoneExecutingLocked(ServiceRecord r, boolean inStopping) { + if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r + + ": nesting=" + r.executeNesting + + ", inStopping=" + inStopping + ", app=" + r.app); r.executeNesting--; if (r.executeNesting <= 0 && r.app != null) { r.app.executingServices.remove(r); @@ -9281,6 +9378,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app); } if (inStopping) { + if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r); mStoppingServices.remove(r); } updateOomAdjLocked(r.app); @@ -11071,61 +11169,64 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { - Iterator<ConnectionRecord> kt + Iterator<ArrayList<ConnectionRecord>> kt = s.connections.values().iterator(); while (kt.hasNext() && adj > FOREGROUND_APP_ADJ) { - // XXX should compute this based on the max of - // all connected clients. - ConnectionRecord cr = kt.next(); - if (cr.binding.client == app) { - // Binding to ourself is not interesting. - continue; - } - if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { - ProcessRecord client = cr.binding.client; - int myHiddenAdj = hiddenAdj; - if (myHiddenAdj > client.hiddenAdj) { - if (client.hiddenAdj >= VISIBLE_APP_ADJ) { - myHiddenAdj = client.hiddenAdj; - } else { - myHiddenAdj = VISIBLE_APP_ADJ; - } + ArrayList<ConnectionRecord> clist = kt.next(); + for (int i=0; i<clist.size() && adj > FOREGROUND_APP_ADJ; i++) { + // XXX should compute this based on the max of + // all connected clients. + ConnectionRecord cr = clist.get(i); + if (cr.binding.client == app) { + // Binding to ourself is not interesting. + continue; } - int clientAdj = computeOomAdjLocked( - client, myHiddenAdj, TOP_APP, true); - if (adj > clientAdj) { - adj = clientAdj >= VISIBLE_APP_ADJ - ? clientAdj : VISIBLE_APP_ADJ; - if (!client.hidden) { - app.hidden = false; + if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) { + ProcessRecord client = cr.binding.client; + int myHiddenAdj = hiddenAdj; + if (myHiddenAdj > client.hiddenAdj) { + if (client.hiddenAdj >= VISIBLE_APP_ADJ) { + myHiddenAdj = client.hiddenAdj; + } else { + myHiddenAdj = VISIBLE_APP_ADJ; + } + } + int clientAdj = computeOomAdjLocked( + client, myHiddenAdj, TOP_APP, true); + if (adj > clientAdj) { + adj = clientAdj >= VISIBLE_APP_ADJ + ? clientAdj : VISIBLE_APP_ADJ; + if (!client.hidden) { + app.hidden = false; + } + app.adjType = "service"; + app.adjTypeCode = ActivityManager.RunningAppProcessInfo + .REASON_SERVICE_IN_USE; + app.adjSource = cr.binding.client; + app.adjTarget = s.name; } + if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { + if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { + schedGroup = Process.THREAD_GROUP_DEFAULT; + } + } + } + ActivityRecord a = cr.activity; + //if (a != null) { + // Slog.i(TAG, "Connection to " + a ": state=" + a.state); + //} + if (a != null && adj > FOREGROUND_APP_ADJ && + (a.state == ActivityState.RESUMED + || a.state == ActivityState.PAUSING)) { + adj = FOREGROUND_APP_ADJ; + schedGroup = Process.THREAD_GROUP_DEFAULT; + app.hidden = false; app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; - app.adjSource = cr.binding.client; + app.adjSource = a; app.adjTarget = s.name; } - if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) { - if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) { - schedGroup = Process.THREAD_GROUP_DEFAULT; - } - } - } - ActivityRecord a = cr.activity; - //if (a != null) { - // Slog.i(TAG, "Connection to " + a ": state=" + a.state); - //} - if (a != null && adj > FOREGROUND_APP_ADJ && - (a.state == ActivityState.RESUMED - || a.state == ActivityState.PAUSING)) { - adj = FOREGROUND_APP_ADJ; - schedGroup = Process.THREAD_GROUP_DEFAULT; - app.hidden = false; - app.adjType = "service"; - app.adjTypeCode = ActivityManager.RunningAppProcessInfo - .REASON_SERVICE_IN_USE; - app.adjSource = a; - app.adjTarget = s.name; } } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 80a41b7..62be918 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -44,7 +44,7 @@ import java.util.HashSet; /** * An entry in the history stack, representing an activity. */ -class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwner { +class ActivityRecord extends IApplicationToken.Stub { final ActivityManagerService service; // owner final ActivityStack stack; // owner final ActivityInfo info; // all about me @@ -78,8 +78,7 @@ class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwne HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act ArrayList newIntents; // any pending new intents for single-top mode HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold - HashSet<UriPermission> readUriPermissions; // special access to reading uris. - HashSet<UriPermission> writeUriPermissions; // special access to writing uris. + UriPermissionOwner uriPermissions; // current special URI access perms. ProcessRecord app; // if non-null, hosting application Bitmap thumbnail; // icon representation of paused screen CharSequence description; // textual description of paused screen @@ -141,11 +140,15 @@ class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwne if (pendingResults != null) { pw.print(prefix); pw.print("pendingResults="); pw.println(pendingResults); } - if (readUriPermissions != null) { - pw.print(prefix); pw.print("readUriPermissions="); pw.println(readUriPermissions); - } - if (writeUriPermissions != null) { - pw.print(prefix); pw.print("writeUriPermissions="); pw.println(writeUriPermissions); + if (uriPermissions != null) { + if (uriPermissions.readUriPermissions != null) { + pw.print(prefix); pw.print("readUriPermissions="); + pw.println(uriPermissions.readUriPermissions); + } + if (uriPermissions.writeUriPermissions != null) { + pw.print(prefix); pw.print("writeUriPermissions="); + pw.println(uriPermissions.writeUriPermissions); + } } pw.print(prefix); pw.print("launchFailed="); pw.print(launchFailed); pw.print(" haveState="); pw.print(haveState); @@ -301,6 +304,13 @@ class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwne } } + UriPermissionOwner getUriPermissionsLocked() { + if (uriPermissions == null) { + uriPermissions = new UriPermissionOwner(service, this); + } + return uriPermissions; + } + void addResultLocked(ActivityRecord from, String resultWho, int requestCode, int resultCode, Intent resultData) { @@ -350,7 +360,7 @@ class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwne intent = new Intent(intent); ar.add(intent); service.grantUriPermissionFromIntentLocked(callingUid, packageName, - intent, this); + intent, getUriPermissionsLocked()); app.thread.scheduleNewIntent(ar, this); sent = true; } catch (RemoteException e) { @@ -367,27 +377,9 @@ class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwne } void removeUriPermissionsLocked() { - if (readUriPermissions != null) { - for (UriPermission perm : readUriPermissions) { - perm.readOwners.remove(this); - if (perm.readOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - service.removeUriPermissionIfNeededLocked(perm); - } - } - readUriPermissions = null; - } - if (writeUriPermissions != null) { - for (UriPermission perm : writeUriPermissions) { - perm.writeOwners.remove(this); - if (perm.writeOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - service.removeUriPermissionIfNeededLocked(perm); - } - } - writeUriPermissions = null; + if (uriPermissions != null) { + uriPermissions.removeUriPermissionsLocked(); + uriPermissions = null; } } @@ -578,38 +570,6 @@ class ActivityRecord extends IApplicationToken.Stub implements UriPermissionOwne state == ActivityState.RESUMED; } - @Override - public void addReadPermission(UriPermission perm) { - if (readUriPermissions == null) { - readUriPermissions = new HashSet<UriPermission>(); - } - readUriPermissions.add(perm); - } - - @Override - public void addWritePermission(UriPermission perm) { - if (writeUriPermissions == null) { - writeUriPermissions = new HashSet<UriPermission>(); - } - writeUriPermissions.add(perm); - } - - @Override - public void removeReadPermission(UriPermission perm) { - readUriPermissions.remove(perm); - if (readUriPermissions.size() == 0) { - readUriPermissions = null; - } - } - - @Override - public void removeWritePermission(UriPermission perm) { - writeUriPermissions.remove(perm); - if (writeUriPermissions.size() == 0) { - writeUriPermissions = null; - } - } - public String toString() { if (stringName != null) { return stringName; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index a5f7e96..a99b48c 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -2327,12 +2327,12 @@ public class ActivityStack { if (grantedUriPermissions != null && callingUid > 0) { for (int i=0; i<grantedUriPermissions.length; i++) { mService.grantUriPermissionLocked(callingUid, r.packageName, - grantedUriPermissions[i], grantedMode, r); + grantedUriPermissions[i], grantedMode, r.getUriPermissionsLocked()); } } mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, - intent, r); + intent, r.getUriPermissionsLocked()); if (newTask) { EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.task.taskId); @@ -2557,7 +2557,7 @@ public class ActivityStack { if (callingUid > 0) { mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName, - data, r); + data, r.getUriPermissionsLocked()); } if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r @@ -2885,7 +2885,7 @@ public class ActivityStack { + " res=" + resultCode + " data=" + resultData); if (r.info.applicationInfo.uid > 0) { mService.grantUriPermissionFromIntentLocked(r.info.applicationInfo.uid, - r.packageName, resultData, r); + r.packageName, resultData, r.getUriPermissionsLocked()); } resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData); diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 255fbe3..f35a68e 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -72,8 +72,8 @@ class ServiceRecord extends Binder { final HashMap<Intent.FilterComparison, IntentBindRecord> bindings = new HashMap<Intent.FilterComparison, IntentBindRecord>(); // All active bindings to the service. - final HashMap<IBinder, ConnectionRecord> connections - = new HashMap<IBinder, ConnectionRecord>(); + final HashMap<IBinder, ArrayList<ConnectionRecord>> connections + = new HashMap<IBinder, ArrayList<ConnectionRecord>>(); // IBinder -> ConnectionRecord of all bound clients ProcessRecord app; // where this service is running or null. @@ -96,7 +96,7 @@ class ServiceRecord extends Binder { String stringName; // caching of toString - static class StartItem implements UriPermissionOwner { + static class StartItem { final ServiceRecord sr; final int id; final Intent intent; @@ -104,12 +104,10 @@ class ServiceRecord extends Binder { long deliveredTime; int deliveryCount; int doneExecutingCount; + UriPermissionOwner uriPermissions; String stringName; // caching of toString - HashSet<UriPermission> readUriPermissions; // special access to reading uris. - HashSet<UriPermission> writeUriPermissions; // special access to writing uris. - StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) { sr = _sr; id = _id; @@ -117,60 +115,17 @@ class ServiceRecord extends Binder { targetPermissionUid = _targetPermissionUid; } - void removeUriPermissionsLocked() { - if (readUriPermissions != null) { - for (UriPermission perm : readUriPermissions) { - perm.readOwners.remove(this); - if (perm.readOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; - sr.ams.removeUriPermissionIfNeededLocked(perm); - } - } - readUriPermissions = null; - } - if (writeUriPermissions != null) { - for (UriPermission perm : writeUriPermissions) { - perm.writeOwners.remove(this); - if (perm.writeOwners.size() == 0 && (perm.globalModeFlags - &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { - perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - sr.ams.removeUriPermissionIfNeededLocked(perm); - } - } - writeUriPermissions = null; + UriPermissionOwner getUriPermissionsLocked() { + if (uriPermissions == null) { + uriPermissions = new UriPermissionOwner(sr.ams, this); } + return uriPermissions; } - @Override - public void addReadPermission(UriPermission perm) { - if (readUriPermissions == null) { - readUriPermissions = new HashSet<UriPermission>(); - } - readUriPermissions.add(perm); - } - - @Override - public void addWritePermission(UriPermission perm) { - if (writeUriPermissions == null) { - writeUriPermissions = new HashSet<UriPermission>(); - } - writeUriPermissions.add(perm); - } - - @Override - public void removeReadPermission(UriPermission perm) { - readUriPermissions.remove(perm); - if (readUriPermissions.size() == 0) { - readUriPermissions = null; - } - } - - @Override - public void removeWritePermission(UriPermission perm) { - writeUriPermissions.remove(perm); - if (writeUriPermissions.size() == 0) { - writeUriPermissions = null; + void removeUriPermissionsLocked() { + if (uriPermissions != null) { + uriPermissions.removeUriPermissionsLocked(); + uriPermissions = null; } } @@ -218,13 +173,15 @@ class ServiceRecord extends Binder { pw.print(prefix); pw.print(" targetPermissionUid="); pw.println(si.targetPermissionUid); } - if (si.readUriPermissions != null) { - pw.print(prefix); pw.print(" readUriPermissions="); - pw.println(si.readUriPermissions); - } - if (si.writeUriPermissions != null) { - pw.print(prefix); pw.print(" writeUriPermissions="); - pw.println(si.writeUriPermissions); + if (si.uriPermissions != null) { + if (si.uriPermissions.readUriPermissions != null) { + pw.print(prefix); pw.print(" readUriPermissions="); + pw.println(si.uriPermissions.readUriPermissions); + } + if (si.uriPermissions.writeUriPermissions != null) { + pw.print(prefix); pw.print(" writeUriPermissions="); + pw.println(si.uriPermissions.writeUriPermissions); + } } } } @@ -296,10 +253,12 @@ class ServiceRecord extends Binder { } if (connections.size() > 0) { pw.print(prefix); pw.println("All Connections:"); - Iterator<ConnectionRecord> it = connections.values().iterator(); + Iterator<ArrayList<ConnectionRecord>> it = connections.values().iterator(); while (it.hasNext()) { - ConnectionRecord c = it.next(); - pw.print(prefix); pw.print(" "); pw.println(c); + ArrayList<ConnectionRecord> c = it.next(); + for (int i=0; i<c.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(c.get(i)); + } } } } diff --git a/services/java/com/android/server/am/UriPermission.java b/services/java/com/android/server/am/UriPermission.java index 93c59cc..c95546e 100644 --- a/services/java/com/android/server/am/UriPermission.java +++ b/services/java/com/android/server/am/UriPermission.java @@ -22,13 +22,14 @@ import android.net.Uri; import java.io.PrintWriter; import java.util.HashSet; -interface UriPermissionOwner { - void addReadPermission(UriPermission perm); - void addWritePermission(UriPermission perm); - void removeReadPermission(UriPermission perm); - void removeWritePermission(UriPermission perm); -} - +/** + * Description of a permission granted to an app to access a particular URI. + * + * CTS tests for this functionality can be run with "runtest cts-appsecurity". + * + * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert + * /src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java + */ class UriPermission { final int uid; final Uri uri; diff --git a/services/java/com/android/server/am/UriPermissionOwner.java b/services/java/com/android/server/am/UriPermissionOwner.java new file mode 100644 index 0000000..99c82e6 --- /dev/null +++ b/services/java/com/android/server/am/UriPermissionOwner.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import android.content.Intent; +import android.net.Uri; +import android.os.Binder; +import android.os.IBinder; + +import java.util.HashSet; +import java.util.Iterator; + +class UriPermissionOwner { + final ActivityManagerService service; + final Object owner; + + Binder externalToken; + + HashSet<UriPermission> readUriPermissions; // special access to reading uris. + HashSet<UriPermission> writeUriPermissions; // special access to writing uris. + + class ExternalToken extends Binder { + UriPermissionOwner getOwner() { + return UriPermissionOwner.this; + } + } + + UriPermissionOwner(ActivityManagerService _service, Object _owner) { + service = _service; + owner = _owner; + } + + Binder getExternalTokenLocked() { + if (externalToken != null) { + externalToken = new ExternalToken(); + } + return externalToken; + } + + static UriPermissionOwner fromExternalToken(IBinder token) { + if (token instanceof ExternalToken) { + return ((ExternalToken)token).getOwner(); + } + return null; + } + + void removeUriPermissionsLocked() { + removeUriPermissionsLocked(Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + + void removeUriPermissionsLocked(int mode) { + if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 + && readUriPermissions != null) { + for (UriPermission perm : readUriPermissions) { + perm.readOwners.remove(this); + if (perm.readOwners.size() == 0 && (perm.globalModeFlags + &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { + perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; + service.removeUriPermissionIfNeededLocked(perm); + } + } + readUriPermissions = null; + } + if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 + && writeUriPermissions != null) { + for (UriPermission perm : writeUriPermissions) { + perm.writeOwners.remove(this); + if (perm.writeOwners.size() == 0 && (perm.globalModeFlags + &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { + perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + service.removeUriPermissionIfNeededLocked(perm); + } + } + writeUriPermissions = null; + } + } + + void removeUriPermissionLocked(Uri uri, int mode) { + if ((mode&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 + && readUriPermissions != null) { + Iterator<UriPermission> it = readUriPermissions.iterator(); + while (it.hasNext()) { + UriPermission perm = it.next(); + if (uri.equals(perm.uri)) { + perm.readOwners.remove(this); + if (perm.readOwners.size() == 0 && (perm.globalModeFlags + &Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0) { + perm.modeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION; + service.removeUriPermissionIfNeededLocked(perm); + } + it.remove(); + } + } + if (readUriPermissions.size() == 0) { + readUriPermissions = null; + } + } + if ((mode&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 + && writeUriPermissions != null) { + Iterator<UriPermission> it = writeUriPermissions.iterator(); + while (it.hasNext()) { + UriPermission perm = it.next(); + if (uri.equals(perm.uri)) { + perm.writeOwners.remove(this); + if (perm.writeOwners.size() == 0 && (perm.globalModeFlags + &Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0) { + perm.modeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + service.removeUriPermissionIfNeededLocked(perm); + } + it.remove(); + } + } + if (writeUriPermissions.size() == 0) { + writeUriPermissions = null; + } + } + } + + public void addReadPermission(UriPermission perm) { + if (readUriPermissions == null) { + readUriPermissions = new HashSet<UriPermission>(); + } + readUriPermissions.add(perm); + } + + public void addWritePermission(UriPermission perm) { + if (writeUriPermissions == null) { + writeUriPermissions = new HashSet<UriPermission>(); + } + writeUriPermissions.add(perm); + } + + public void removeReadPermission(UriPermission perm) { + readUriPermissions.remove(perm); + if (readUriPermissions.size() == 0) { + readUriPermissions = null; + } + } + + public void removeWritePermission(UriPermission perm) { + writeUriPermissions.remove(perm); + if (writeUriPermissions.size() == 0) { + writeUriPermissions = null; + } + } + + @Override + public String toString() { + return owner.toString(); + } +} diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java index 1142136..3dcaff6 100644 --- a/services/java/com/android/server/sip/SipService.java +++ b/services/java/com/android/server/sip/SipService.java @@ -32,6 +32,7 @@ import android.net.sip.SipProfile; import android.net.sip.SipSessionAdapter; import android.net.sip.SipSessionState; import android.net.wifi.WifiManager; +import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; @@ -97,6 +98,7 @@ public final class SipService extends ISipService.Stub { } public void open(SipProfile localProfile) { + localProfile.setCallingUid(Binder.getCallingUid()); if (localProfile.getAutoRegistration()) { openToReceiveCalls(localProfile); } else { @@ -119,6 +121,7 @@ public final class SipService extends ISipService.Stub { public synchronized void open3(SipProfile localProfile, String incomingCallBroadcastAction, ISipSessionListener listener) { + localProfile.setCallingUid(Binder.getCallingUid()); if (TextUtils.isEmpty(incomingCallBroadcastAction)) { throw new RuntimeException( "empty broadcast action for incoming call"); @@ -165,6 +168,7 @@ public final class SipService extends ISipService.Stub { public synchronized ISipSession createSession(SipProfile localProfile, ISipSessionListener listener) { + localProfile.setCallingUid(Binder.getCallingUid()); if (!mConnected) return null; try { SipSessionGroupExt group = createGroup(localProfile); @@ -362,16 +366,7 @@ public final class SipService extends ISipService.Stub { private SipProfile duplicate(SipProfile p) { try { - return new SipProfile.Builder(p.getUserName(), p.getSipDomain()) - .setProfileName(p.getProfileName()) - .setPassword("*") - .setPort(p.getPort()) - .setProtocol(p.getProtocol()) - .setOutboundProxy(p.getProxyAddress()) - .setSendKeepAlive(p.getSendKeepAlive()) - .setAutoRegistration(p.getAutoRegistration()) - .setDisplayName(p.getDisplayName()) - .build(); + return new SipProfile.Builder(p).setPassword("*").build(); } catch (Exception e) { Log.wtf(TAG, "duplicate()", e); throw new RuntimeException("duplicate profile", e); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 6f3c66d..695cbfa 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -162,9 +162,13 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, const uint32_t hwFlags = hw.getFlags(); mFormat = format; - mReqFormat = format; mWidth = w; mHeight = h; + + mReqFormat = format; + mReqWidth = w; + mReqHeight = h; + mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; @@ -196,12 +200,16 @@ void Layer::reloadTexture(const Region& dirty) } else { slowpath: GGLSurface t; - status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); - LOGE_IF(res, "error %d (%s) locking buffer %p", - res, strerror(res), buffer.get()); - if (res == NO_ERROR) { - mBufferManager.loadTexture(dirty, t); - buffer->unlock(); + if (buffer->usage & GRALLOC_USAGE_SW_READ_MASK) { + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + mBufferManager.loadTexture(dirty, t); + buffer->unlock(); + } + } else { + // we can't do anything } } } @@ -300,16 +308,22 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, uint32_t w, h, f; { // scope for the lock Mutex::Autolock _l(mLock); - const bool fixedSizeChanged = mFixedSize != (reqWidth && reqHeight); - const bool formatChanged = mReqFormat != reqFormat; - mReqWidth = reqWidth; - mReqHeight = reqHeight; - mReqFormat = reqFormat; - mFixedSize = reqWidth && reqHeight; - w = reqWidth ? reqWidth : mWidth; - h = reqHeight ? reqHeight : mHeight; - f = reqFormat ? reqFormat : mFormat; - if (fixedSizeChanged || formatChanged) { + + // zero means default + if (!reqFormat) reqFormat = mFormat; + if (!reqWidth) reqWidth = mWidth; + if (!reqHeight) reqHeight = mHeight; + + w = reqWidth; + h = reqHeight; + f = reqFormat; + + if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) || + (reqFormat != mReqFormat)) { + mReqWidth = reqWidth; + mReqHeight = reqHeight; + mReqFormat = reqFormat; + lcblk->reallocateAllExcept(index); } } diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java index 980affa..12df44e 100644 --- a/telephony/java/com/android/internal/telephony/CallManager.java +++ b/telephony/java/com/android/internal/telephony/CallManager.java @@ -691,8 +691,8 @@ public final class CallManager { * Stop the playing DTMF tone. Ignored if there is no playing DTMF * tone or no active call. */ - public void stopDtmf(Phone phone) { - phone.stopDtmf(); + public void stopDtmf() { + if (hasActiveFgCall()) getFgPhone().stopDtmf(); } /** @@ -709,7 +709,7 @@ public final class CallManager { * @param onComplete is the callback message when the action is processed by BP * */ - public boolean sendBurstDtmf(Phone phone, String dtmfString, int on, int off, Message onComplete) { + public boolean sendBurstDtmf(String dtmfString, int on, int off, Message onComplete) { if (hasActiveFgCall()) { getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete); return true; diff --git a/tests/CoreTests/android/core/JavaPerformanceTests.java b/tests/CoreTests/android/core/JavaPerformanceTests.java index fbe70cc..95075ea 100644 --- a/tests/CoreTests/android/core/JavaPerformanceTests.java +++ b/tests/CoreTests/android/core/JavaPerformanceTests.java @@ -23,7 +23,6 @@ public class JavaPerformanceTests { public static String[] children() { return new String[] { - StringTest.class.getName(), HashMapPerformanceTest.class.getName(), ArrayListPerformanceTest.class.getName(), TreeMapPerformanceTest.class.getName(), diff --git a/tests/CoreTests/android/core/StringTest.java b/tests/CoreTests/android/core/StringTest.java deleted file mode 100644 index 128531c..0000000 --- a/tests/CoreTests/android/core/StringTest.java +++ /dev/null @@ -1,951 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.core; - -import java.util.Locale; - -import android.test.PerformanceTestBase; -import android.test.PerformanceTestCase; - -public class StringTest extends PerformanceTestBase { - public static final int ITERATIONS = 1000; - public static final String STATIC_STRING_01 = "Hello Android"; - public static final String STATIC_STRING_02 = - "Remember, today is the tomorrow you worried about yesterday"; - public static final char[] STATIC_CHAR_ARRAY = - {'N', 'A', 'N', 'D', 'R', 'O', 'I', 'D'}; - public static StringBuffer STATIC_SBUF = new StringBuffer(STATIC_STRING_02); - - @Override - public int startPerformance(PerformanceTestCase.Intermediates intermediates) { - intermediates.setInternalIterations(ITERATIONS); - return 0; - } - - /** Create an empty String object* */ - - public void testStringCreate() { - String rString; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - rString = new String(); - } - } - - /** Create an initialised String object* */ - - public void testStringCreate1() { - String rString, str = STATIC_STRING_01; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); - rString = new String(str); // 10 - } - } - - /** equals() with for loop* */ - public void testStringEquals() { - String mString = new String(STATIC_STRING_01); - String str = STATIC_STRING_01; - boolean result; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - result = mString.equals(str); - } - } - - /** - * ContentEquals- Comparing the content of a String with that of a String - * Buffer* - */ - - public void testStringContentEquals() { - StringBuffer sBuf = new StringBuffer(STATIC_STRING_01); - String str = STATIC_STRING_01; - boolean result; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - result = str.contentEquals(sBuf); - } - } - - /** Compare string objects lexicographically using compareTo() with for loop* */ - - public void testStringCompareTo() { - String str1 = new String(STATIC_STRING_01); - String str2 = STATIC_STRING_01; - int result; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - result = str1.compareTo(str2); - } - - } - - /** Compare string objects using compareToIgnorecase() with for loop* */ - - public void testStringCompareToIgnoreCase() { - String mString = new String(STATIC_STRING_01); - String str2 = STATIC_STRING_01; - int result; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - result = mString.compareToIgnoreCase(str2); - } - } - - /** startsWith * */ - - public void testStringstartsWith() { - boolean result; - String str1 = STATIC_STRING_02, str2 = "Rem"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - result = str1.startsWith(str2); - } - } - - /** startsWith(String seq, int begin) * */ - - public void testStringstartsWith1() { - String str1 = STATIC_STRING_02, str2 = "tom"; - int pos = 10; - boolean result; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - result = str1.startsWith(str2, pos); - } - } - - /** endsWith * */ - - public void testStringendsWith() { - String str = STATIC_STRING_02, str1 = "day"; - boolean result; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - result = str.endsWith(str1); - } - } - - /** - * indexOf to determine whether a string contains a substring - */ - public void testStringindexOf() { - boolean result; - String str = STATIC_STRING_02, str1 = "tomo"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - result = str.indexOf(str1) > 0; - } - } - - /** indexOf()* */ - - public void testStringindexOf1() { - int index; - String str = STATIC_STRING_02; - char c = 't'; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - index = str.indexOf(c); - } - - } - - /** indexOf(char c, int start)* */ - public void testStringindexOf2() { - int index, pos = 12; - String str = STATIC_STRING_02, str1 = "tom"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - index = str.indexOf(str1, pos); - } - } - - /** lastIndexOf()* */ - - public void testStringlastIndexOf() { - int index; - char c = 't'; - String str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - index = str.lastIndexOf(c); - } - } - - /** lastIndexOf()* */ - - public void testStringlastIndexOf1() { - int index, pos = 36; - String str = STATIC_STRING_02, str1 = "tom"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - index = str.lastIndexOf(str1, pos); - } - } - - /** - * contains() to determine whether a string contains a substring - */ - - public void testStringcontains() { - boolean result; - String str = STATIC_STRING_02, str1 = "tomo"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - result = str.contains(str1); - } - } - - /** substring(int start) */ - - public void testStringsubstring() { - String rString; - String str = STATIC_STRING_02; - int index = 10; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - rString = str.substring(index); - } - } - - /** substring(int start, int end) in a for loop* */ - - public void testStringsubstring1() { - String rString; - String str = STATIC_STRING_02; - int start = 10, end = 48; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - rString = str.substring(start, end); - } - } - - /** - * valueOf(char[] cArray) String representation of a character array - */ - public void testStringvalueOf() { - String rString; - char[] cArray = STATIC_CHAR_ARRAY; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - rString = String.valueOf(cArray); - } - } - - /** valueOf(char[] cArray, int offset, int count)* */ - - public void testStringvalueOf1() { - String rString; - char[] cArray = STATIC_CHAR_ARRAY; - int start = 1, end = 7; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - rString = String.valueOf(cArray, start, end); - } - } - - /** Convert a string to a char Array* */ - - public void testStringtoCharArray() { - char[] cArray; - String str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - cArray = str.toCharArray(); - } - } - - /** length()* */ - - public void testStringlength() { - int len; - String str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - len = str.length(); - } - } - - /** hashcode()* */ - - public void testStringhashCode() { - int index; - String str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - index = str.hashCode(); - } - } - - /** replace()* */ - - public void testStringreplace() { - String rString; - String str = STATIC_STRING_02; - char c1 = ' ', c2 = ' '; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - rString = str.replace(c1, c2); - } - } - - public void testStringreplaceAll() { - String rString; - String str = STATIC_STRING_02, str1 = " ", str2 = "/"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - rString = str.replaceAll(str1, str2); - } - } - - /** Convert a StringBuffer to a String* */ - - public void testStringtoString() { - StringBuffer sBuf = new StringBuffer(STATIC_STRING_02); - - String rString; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - rString = sBuf.toString(); - } - } - - /** Split a string into an array of strings* */ - - public void testStringsplit() { - String[] strings; - String str1 = STATIC_STRING_02, str = " "; - for (int i = ITERATIONS - 1; i >= 0; i--) { - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - strings = str1.split(str); - - } - } - - /** Split a string into an array of strings* */ - - public void testStringsplit1() { - String str = STATIC_STRING_02, str1 = " "; - String[] strings; - int pos = 8; - for (int i = ITERATIONS - 1; i >= 0; i--) { - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - strings = str.split(str1, pos); - } - } - - public void testStringgetBytes() { - byte[] bytes; - String str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - bytes = str.getBytes(); - } - } - - /** copyValueOf(char[] data) * */ - - public void testStringcopyValueOf() { - String rString; - char[] cArray = STATIC_CHAR_ARRAY; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - rString = String.copyValueOf(cArray); - } - } - - /** copyValueOf(char[] data, int index, int count)* */ - - public void testStringcopyValueOf1() { - String rString; - int start = 1, end = 7; - char[] cArray = STATIC_CHAR_ARRAY; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - rString = String.copyValueOf(cArray, start, end); - } - } - - /** trim()* */ - - public void testStringtrim() { - String mString = - new String( - " HELLO ANDROID "); - String rString; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - rString = mString.trim(); - } - } - - /** getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)* */ - - public void testStringgetChars() { - char[] cArray = STATIC_CHAR_ARRAY; - String str = STATIC_STRING_01; - int value1 = 7, value2 = 12, value3 = 1; - for (int i = ITERATIONS - 1; i >= 0; i--) { - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - str.getChars(value1, value2, cArray, value3); - } - } - - /** toUpperCase()* */ - - public void testStringtoUpperCase() { - String rString, str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - rString = str.toUpperCase(); - } - } - - /** toUpperCase() with locale* */ - - public void testStringtoUpperCase1() { - Locale locale = new Locale("tr"); - String str = STATIC_STRING_02; - String rString; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - rString = str.toUpperCase(locale); - } - } - - /** toLowerCase* */ - - public void StringtoLowerCase() { - String rString, str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - rString = str.toLowerCase(); - } - } - - /** toLowerCase with locale* */ - - public void testStringtoLowerCase1() { - Locale locale = new Locale("tr"); - String rString, str = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - rString = str.toLowerCase(locale); - } - } - - /** charAt()* */ - - public void testStringcharAt() { - String str = STATIC_STRING_02; - int index, pos = 21; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - index = str.charAt(pos); - } - } - - public void testStringConcat() { - String mString, str1 = STATIC_STRING_01, str2 = STATIC_STRING_02; - for (int i = ITERATIONS - 1; i >= 0; i--) { - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - mString = str1.concat(str2); - } - } - - public void testStringBufferAppend() { - StringBuffer sBuf = new StringBuffer(" "); - for (int i = ITERATIONS - 1; i >= 0; i--) { - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - sBuf.append(i); - } - } - - public void testStringBufferInsert() { - StringBuffer sBuf = new StringBuffer(" "); - int index = sBuf.length(); - for (int i = ITERATIONS - 1; i >= 0; i--) { - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - sBuf.insert(index, i); - } - } - - public void testStringBufferReverse() { - StringBuffer sBuf = STATIC_SBUF; - for (int i = ITERATIONS - 1; i >= 0; i--) { - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - sBuf.reverse(); - } - } - - public void testStringBufferSubstring() { - StringBuffer sBuf = STATIC_SBUF; - String rString; - int index = 0; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - rString = sBuf.substring(index); - } - } - - public void testStringBufferSubstring1() { - StringBuffer sBuf = STATIC_SBUF; - String rString; - int start = 5, end = 25; - for (int i = ITERATIONS - 1; i >= 0; i--) { - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - rString = sBuf.substring(start, end); - } - } - - public void testStringBufferReplace() { - StringBuffer sBuf = STATIC_SBUF; - int start = 3, end = 6; - String str = "ind"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - sBuf.replace(start, end, str); - } - } - - public void testStringBufferIndexOf() { - StringBuffer sBuf = STATIC_SBUF; - String str = "t"; - int index; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - index = sBuf.indexOf(str); - } - } - - public void testStringBufferIndexOf1() { - StringBuffer sBuf = STATIC_SBUF; - String str = "tom"; - int index, pos = 12; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - index = sBuf.indexOf(str, pos); - } - - } - - public void testStringBufferLastIndexOf() { - StringBuffer sBuf = STATIC_SBUF; - String str = "t"; - int index; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - index = sBuf.lastIndexOf(str); - } - } - - public void testStringBufferLastIndexOf1() { - StringBuffer sBuf = STATIC_SBUF; - int index, pos = 36; - String str = "tom"; - for (int i = ITERATIONS - 1; i >= 0; i--) { - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - index = sBuf.lastIndexOf(str, pos); - } - } -} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java index 0c2347d..716086b 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/WebViewEventSender.java @@ -16,14 +16,12 @@ package com.android.dumprendertree; -import android.os.Handler; import android.os.SystemClock; import android.util.*; import android.view.KeyEvent; import android.view.MotionEvent; import android.webkit.WebView; -import java.lang.InterruptedException; import java.util.Arrays; import java.util.Vector; @@ -33,7 +31,7 @@ public class WebViewEventSender implements EventSender { WebViewEventSender(WebView webView) { mWebView = webView; - mTouchPoints = new Vector(); + mTouchPoints = new Vector<TouchPoint>(); } public void resetMouse() { @@ -82,47 +80,23 @@ public class WebViewEventSender implements EventSender { mouseUp(); } - public void mouseDown() { - /* KeyEvent event = new KeyEvent( - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); - mWebView.onKeyDown(event.getKeyCode(), event); */ - } + public void mouseDown() { + long ts = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mouseX, mouseY, 0); + mWebView.onTouchEvent(event); + } - public void mouseMoveTo(int X, int Y) { - if (X > mouseX) { - KeyEvent event = new KeyEvent( - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT); - mWebView.onKeyDown(event.getKeyCode(), event); - mWebView.onKeyUp(event.getKeyCode(), event); - } else if ( X < mouseX ) { - KeyEvent event = new KeyEvent( - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT); - mWebView.onKeyDown(event.getKeyCode(), event); - mWebView.onKeyUp(event.getKeyCode(), event); - } - if (Y > mouseY) { - KeyEvent event = new KeyEvent( - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN); - mWebView.onKeyDown(event.getKeyCode(), event); - mWebView.onKeyUp(event.getKeyCode(), event); - } else if (Y < mouseY ) { - KeyEvent event = new KeyEvent( - KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP); - mWebView.onKeyDown(event.getKeyCode(), event); - mWebView.onKeyUp(event.getKeyCode(), event); - } - mouseX= X; - mouseY= Y; - - } + public void mouseMoveTo(int X, int Y) { + mouseX= X; + mouseY= Y; + } - public void mouseUp() { - /* KeyEvent event = new KeyEvent( - KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); - mWebView.onKeyDown(event.getKeyCode(), event);*/ + public void mouseUp() { + long ts = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mouseX, mouseY, 0); + mWebView.onTouchEvent(event); + } - } - // Assumes lowercase chars, case needs to be // handled by calling function. static int keyMapper(char c) { @@ -365,7 +339,7 @@ public class WebViewEventSender implements EventSender { mX = x; mY = y; } - }; + } private Vector<TouchPoint> mTouchPoints; private int mTouchMetaState; diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index b339a2c..094b7db 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -41,7 +41,7 @@ LOCAL_STATIC_LIBRARIES := \ libpng ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt +LOCAL_LDLIBS += -lrt -lpthread endif # Statically link libz for MinGW (Win SDK under Linux), diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 35124aa..b0f086b 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -141,9 +141,9 @@ int doList(Bundle* bundle) if (bundle->getVerbose()) { printf("Archive: %s\n", zipFileName); printf( - " Length Method Size Ratio Date Time CRC-32 Name\n"); + " Length Method Size Ratio Offset Date Time CRC-32 Name\n"); printf( - "-------- ------ ------- ----- ---- ---- ------ ----\n"); + "-------- ------ ------- ----- ------- ---- ---- ------ ----\n"); } totalUncLen = totalCompLen = 0; @@ -159,12 +159,13 @@ int doList(Bundle* bundle) strftime(dateBuf, sizeof(dateBuf), "%m-%d-%y %H:%M", localtime(&when)); - printf("%8ld %-7.7s %7ld %3d%% %s %08lx %s\n", + printf("%8ld %-7.7s %7ld %3d%% %8zd %s %08lx %s\n", (long) entry->getUncompressedLen(), compressionName(entry->getCompressionMethod()), (long) entry->getCompressedLen(), calcPercent(entry->getUncompressedLen(), entry->getCompressedLen()), + (size_t) entry->getLFHOffset(), dateBuf, entry->getCRC32(), entry->getFileName()); diff --git a/tools/aapt/ZipEntry.h b/tools/aapt/ZipEntry.h index 7f721b4..c2f3227 100644 --- a/tools/aapt/ZipEntry.h +++ b/tools/aapt/ZipEntry.h @@ -72,6 +72,11 @@ public: off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } /* + * Return the offset of the local file header. + */ + off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } + + /* * Return the absolute file offset of the start of the compressed or * uncompressed data. */ @@ -186,11 +191,6 @@ protected: void setModWhen(time_t when); /* - * Return the offset of the local file header. - */ - off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } - - /* * Set the offset of the local file header, relative to the start of * the current file. */ diff --git a/tools/localize/Android.mk b/tools/localize/Android.mk index ab79f8d..f284e86 100644 --- a/tools/localize/Android.mk +++ b/tools/localize/Android.mk @@ -34,7 +34,7 @@ LOCAL_STATIC_LIBRARIES := \ libcutils ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt +LOCAL_LDLIBS += -lrt -lpthread endif diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java index b8ac6d7..5789cd4 100644 --- a/voip/java/android/net/sip/SipAudioCallImpl.java +++ b/voip/java/android/net/sip/SipAudioCallImpl.java @@ -36,6 +36,7 @@ import android.net.sip.SipManager; import android.net.sip.SipProfile; import android.net.sip.SipSessionAdapter; import android.net.sip.SipSessionState; +import android.net.wifi.WifiManager; import android.os.Message; import android.os.RemoteException; import android.os.Vibrator; @@ -84,10 +85,13 @@ public class SipAudioCallImpl extends SipSessionAdapter private ToneGenerator mRingbackTone; private SipProfile mPendingCallRequest; + private WifiManager mWm; + private WifiManager.WifiLock mWifiHighPerfLock; public SipAudioCallImpl(Context context, SipProfile localProfile) { mContext = context; mLocalProfile = localProfile; + mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); } public void setListener(SipAudioCall.Listener listener) { @@ -422,6 +426,28 @@ public class SipAudioCallImpl extends SipSessionAdapter } } + private void grabWifiHighPerfLock() { + if (mWifiHighPerfLock == null) { + Log.v(TAG, "acquire wifi high perf lock"); + mWifiHighPerfLock = ((WifiManager) + mContext.getSystemService(Context.WIFI_SERVICE)) + .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG); + mWifiHighPerfLock.acquire(); + } + } + + private void releaseWifiHighPerfLock() { + if (mWifiHighPerfLock != null) { + Log.v(TAG, "release wifi high perf lock"); + mWifiHighPerfLock.release(); + mWifiHighPerfLock = null; + } + } + + private boolean isWifiOn() { + return (mWm.getConnectionInfo().getBSSID() == null) ? false : true; + } + private SessionDescription createContinueSessionDescription() { return createSdpBuilder(true, mCodec).build(); } @@ -556,7 +582,7 @@ public class SipAudioCallImpl extends SipSessionAdapter private void startCall(SdpSessionDescription peerSd) { stopCall(DONT_RELEASE_SOCKET); - + if (isWifiOn()) grabWifiHighPerfLock(); mPeerSd = peerSd; String peerMediaAddress = peerSd.getPeerMediaAddress(AUDIO); // TODO: handle multiple media fields @@ -623,6 +649,7 @@ public class SipAudioCallImpl extends SipSessionAdapter private void stopCall(boolean releaseSocket) { Log.d(TAG, "stop audiocall"); + releaseWifiHighPerfLock(); if (mAudioStream != null) { mAudioStream.join(null); diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java index e71c293..6c99141 100644 --- a/voip/java/android/net/sip/SipProfile.java +++ b/voip/java/android/net/sip/SipProfile.java @@ -35,7 +35,7 @@ import javax.sip.address.URI; * Class containing a SIP account, domain and server information. * @hide */ -public class SipProfile implements Parcelable, Serializable { +public class SipProfile implements Parcelable, Serializable, Cloneable { private static final long serialVersionUID = 1L; private static final int DEFAULT_PORT = 5060; private Address mAddress; @@ -46,6 +46,8 @@ public class SipProfile implements Parcelable, Serializable { private String mProfileName; private boolean mSendKeepAlive = false; private boolean mAutoRegistration = true; + private boolean mAllowOutgoingCall = false; + private int mCallingUid = -1; /** @hide */ public static final Parcelable.Creator<SipProfile> CREATOR = @@ -79,6 +81,23 @@ public class SipProfile implements Parcelable, Serializable { } /** + * Creates a builder based on the given profile. + */ + public Builder(SipProfile profile) { + if (profile == null) throw new NullPointerException(); + try { + mProfile = (SipProfile) profile.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("should not occur", e); + } + mProfile.mAddress = null; + mUri = profile.getUri(); + mUri.setUserPassword(profile.getPassword()); + mDisplayName = profile.getDisplayName(); + mProxyAddress = profile.getProxyAddress(); + } + + /** * Constructor. * * @param uriString the URI string as "sip:<user_name>@<domain>" @@ -226,6 +245,18 @@ public class SipProfile implements Parcelable, Serializable { } /** + * Sets the allow-outgoing-call flag. + * + * @param flag true if allowing to make outgoing call on the profile; + * false otherwise + * @return this builder object + */ + public Builder setOutgoingCallAllowed(boolean flag) { + mProfile.mAllowOutgoingCall = flag; + return this; + } + + /** * Builds and returns the SIP profile object. * * @return the profile object created @@ -262,6 +293,8 @@ public class SipProfile implements Parcelable, Serializable { mProfileName = in.readString(); mSendKeepAlive = (in.readInt() == 0) ? false : true; mAutoRegistration = (in.readInt() == 0) ? false : true; + mAllowOutgoingCall = (in.readInt() == 0) ? false : true; + mCallingUid = in.readInt(); } /** @hide */ @@ -274,6 +307,8 @@ public class SipProfile implements Parcelable, Serializable { out.writeString(mProfileName); out.writeInt(mSendKeepAlive ? 1 : 0); out.writeInt(mAutoRegistration ? 1 : 0); + out.writeInt(mAllowOutgoingCall ? 1 : 0); + out.writeInt(mCallingUid); } /** @hide */ @@ -398,4 +433,27 @@ public class SipProfile implements Parcelable, Serializable { public boolean getAutoRegistration() { return mAutoRegistration; } + + /** + * Returns true if allowing to make outgoing calls on this profile. + */ + public boolean isOutgoingCallAllowed() { + return mAllowOutgoingCall; + } + + /** + * Sets the calling process's Uid in the sip service. + * @hide + */ + public void setCallingUid(int uid) { + mCallingUid = uid; + } + + /** + * Gets the calling process's Uid in the sip settings. + * @hide + */ + public int getCallingUid() { + return mCallingUid; + } } diff --git a/voip/jni/rtp/Android.mk b/voip/jni/rtp/Android.mk index 3bd85aa..a364355 100644 --- a/voip/jni/rtp/Android.mk +++ b/voip/jni/rtp/Android.mk @@ -32,11 +32,10 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libmedia -LOCAL_STATIC_LIBRARIES := libspeex +LOCAL_STATIC_LIBRARIES := LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) \ - external/speex/include + $(JNI_H_INCLUDE) LOCAL_CFLAGS += -fvisibility=hidden diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp index b5a4e22..bb45a9a 100644 --- a/voip/jni/rtp/AudioGroup.cpp +++ b/voip/jni/rtp/AudioGroup.cpp @@ -40,8 +40,6 @@ #include <media/AudioTrack.h> #include <media/mediarecorder.h> -#include <speex/speex_echo.h> - #include "jni.h" #include "JNIHelp.h" @@ -447,8 +445,6 @@ private: int mDeviceSocket; AudioTrack mTrack; AudioRecord mRecord; - - SpeexEchoState *mEchoState; bool networkLoop(); bool deviceLoop(); @@ -510,7 +506,6 @@ AudioGroup::AudioGroup() mEventQueue = -1; mDtmfEvent = -1; mDeviceSocket = -1; - mEchoState = NULL; mNetworkThread = new NetworkThread(this); mDeviceThread = new DeviceThread(this); } @@ -523,9 +518,6 @@ AudioGroup::~AudioGroup() mRecord.stop(); close(mEventQueue); close(mDeviceSocket); - if (mEchoState) { - speex_echo_state_destroy(mEchoState); - } while (mChain) { AudioStream *next = mChain->mNext; delete mChain; @@ -574,8 +566,7 @@ bool AudioGroup::set(int sampleRate, int sampleCount) } LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency()); - // Initialize echo canceller. - mEchoState = speex_echo_state_init(sampleCount, sampleRate); + // TODO: initialize echo canceler here. // Create device socket. int pair[2]; @@ -642,7 +633,6 @@ bool AudioGroup::setMode(int mode) if (mode == MUTED) { mRecord.stop(); } else { - speex_echo_state_reset(mEchoState); mRecord.start(); } @@ -803,7 +793,7 @@ bool AudioGroup::deviceLoop() status_t status = mRecord.obtainBuffer(&buffer, 1); if (status == NO_ERROR) { - int count = ((int)buffer.frameCount < toRead) ? + int count = (buffer.frameCount < toRead) ? buffer.frameCount : toRead; memcpy(&input[mSampleCount - toRead], buffer.i8, count * 2); toRead -= count; @@ -827,9 +817,8 @@ bool AudioGroup::deviceLoop() if (mMode == NORMAL) { send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT); } else { - int16_t result[mSampleCount]; - speex_echo_cancellation(mEchoState, input, output, result); - send(mDeviceSocket, result, sizeof(result), MSG_DONTWAIT); + // TODO: Echo canceller runs here. + send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT); } } return true; |