From d205d5b57071dd9e53a42184f8a5fc5cd7398209 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Wed, 27 May 2009 11:02:29 -0400 Subject: [PATCH 01/43] add onPause and onResume apis, to inform the view when it can pause its activities associated with the DOM. --- core/java/android/webkit/WebView.java | 50 ++++++++++++++++++++--- core/java/android/webkit/WebViewCore.java | 18 ++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 8fff64440db86..eef128be6a4ed 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -370,6 +370,9 @@ public class WebView extends AbsoluteLayout // Whether or not to draw the focus ring. private boolean mDrawFocusRing = true; + // true if onPause has been called (and not onResume) + private boolean mIsPaused; + /** * Customizable constant */ @@ -997,7 +1000,7 @@ public class WebView extends AbsoluteLayout /** * If platform notifications are enabled, this should be called - * from onPause() or onStop(). + * from the Activity's onPause() or onStop(). */ public static void disablePlatformNotifications() { Network.disablePlatformNotifications(); @@ -1938,21 +1941,58 @@ public class WebView extends AbsoluteLayout } /** - * Pause all layout, parsing, and javascript timers. This can be useful if - * the WebView is not visible or the application has been paused. + * Pause all layout, parsing, and javascript timers for all webviews. This + * is a global requests, not restricted to just this webview. This can be + * useful if the application has been paused. */ public void pauseTimers() { mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS); } /** - * Resume all layout, parsing, and javascript timers. This will resume - * dispatching all timers. + * Resume all layout, parsing, and javascript timers for all webviews. + * This will resume dispatching all timers. */ public void resumeTimers() { mWebViewCore.sendMessage(EventHub.RESUME_TIMERS); } + /** + * Call this to pause any extra processing associated with this view and + * its associated DOM/plugins/javascript/etc. For example, if the view is + * taken offscreen, this could be called to reduce unnecessary CPU and/or + * network traffic. When the view is again "active", call onResume(). + * + * Note that this differs from pauseTimers(), which affects all views/DOMs + * @hide + */ + public void onPause() { + if (!mIsPaused) { + mIsPaused = true; + mWebViewCore.sendMessage(EventHub.ON_PAUSE); + } + } + + /** + * Call this to balanace a previous call to onPause() + * @hide + */ + public void onResume() { + if (mIsPaused) { + mIsPaused = false; + mWebViewCore.sendMessage(EventHub.ON_RESUME); + } + } + + /** + * Returns true if the view is paused, meaning onPause() was called. Calling + * onResume() sets the paused state back to false. + * @hide + */ + public boolean isPaused() { + return mIsPaused; + } + /** * Clear the resource cache. Note that the cache is per-application, so * this will clear the cache for all WebViews used. diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e4d08cf38884b..f9bbc313c8e84 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -591,6 +591,8 @@ final class WebViewCore { "TOUCH_UP", // = 140; "TOUCH_EVENT", // = 141; "SET_ACTIVE", // = 142; + "ON_PAUSE", // = 143 + "ON_RESUME", // = 144 }; class EventHub { @@ -647,6 +649,11 @@ final class WebViewCore { // or not, based on whether the WebView has focus. static final int SET_ACTIVE = 142; + // pause/resume activity for just this DOM (unlike pauseTimers, which + // is global) + static final int ON_PAUSE = 143; + static final int ON_RESUME = 144; + // Network-based messaging static final int CLEAR_SSL_PREF_TABLE = 150; @@ -841,6 +848,14 @@ final class WebViewCore { } break; + case ON_PAUSE: + nativePause(); + break; + + case ON_RESUME: + nativeResume(); + break; + case SET_NETWORK_STATE: if (BrowserFrame.sJavaBridge == null) { throw new IllegalStateException("No WebView " + @@ -1750,4 +1765,7 @@ final class WebViewCore { } } + + private native void nativePause(); + private native void nativeResume(); } From fe08d99725efd0dde7ba67ff1979a04fec2ba99f Mon Sep 17 00:00:00 2001 From: Jason Sams Date: Wed, 27 May 2009 14:45:32 -0700 Subject: [PATCH 02/43] Implement first pass bitmap to allocation support. The Java bindings can create a 2D allocation by passing in a Bitmap object. --- libs/rs/RenderScript.h | 1 + libs/rs/RenderScriptEnv.h | 16 +-- .../com/android/fountain/RenderScript.java | 73 ++++++++++---- libs/rs/jni/RenderScript_jni.cpp | 21 +++- libs/rs/rs.spec | 13 ++- libs/rs/rsAllocation.cpp | 99 ++++++++++++++++++- libs/rs/rsContext.cpp | 2 +- libs/rs/rsElement.cpp | 4 + libs/rs/rsProgramFragment.cpp | 11 ++- libs/rs/rsScriptC.cpp | 42 +++++++- 10 files changed, 249 insertions(+), 33 deletions(-) diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index e1a4b6ded258e..378fcccb35c6f 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -86,6 +86,7 @@ enum RsElementPredefined { RS_ELEMENT_USER_I32, RS_ELEMENT_USER_FLOAT, + RS_ELEMENT_A_8, RS_ELEMENT_RGB_565, RS_ELEMENT_RGBA_5551, RS_ELEMENT_RGBA_4444, diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h index 9ba1a00444c69..dfca56c346795 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/RenderScriptEnv.h @@ -58,12 +58,9 @@ typedef struct { void (*color)(void *con, float r, float g, float b, float a); - void (*renderTriangleMesh)(void *con, RsTriangleMesh); - void (*renderTriangleMeshRange)(void *con, RsTriangleMesh, uint32_t start, uint32_t count); - void (*programFragmentBindTexture)(void *con, RsProgramFragment, uint32_t slot, RsAllocation); void (*programFragmentBindSampler)(void *con, RsProgramFragment, uint32_t slot, RsAllocation); - + void (*materialDiffuse)(void *con, float r, float g, float b, float a); void (*materialSpecular)(void *con, float r, float g, float b, float a); void (*lightPosition)(void *con, float x, float y, float z, float w); @@ -76,11 +73,18 @@ typedef struct { uint32_t (*rand)(void *con, uint32_t max); + void (*contextBindProgramFragment)(void *con, RsProgramFragment pf); + void (*contextBindProgramFragmentStore)(void *con, RsProgramFragmentStore pfs); + + + // Drawing funcs + void (*renderTriangleMesh)(void *con, RsTriangleMesh); + void (*renderTriangleMeshRange)(void *con, RsTriangleMesh, uint32_t start, uint32_t count); + // Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a void (*drawTriangleArray)(void *con, RsAllocation alloc, uint32_t count); - void (*contextBindProgramFragment)(void *con, RsProgramFragment pf); - void (*contextBindProgramFragmentStore)(void *con, RsProgramFragmentStore pfs); + void (*drawRect)(void *con, int32_t x1, int32_t x2, int32_t y1, int32_t y2); } rsc_FunctionTable; typedef void (*rsc_RunScript)(void *con, const rsc_FunctionTable *, uint32_t launchID); diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java index bab966618cffd..ffde1a268b3f6 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java @@ -24,6 +24,8 @@ import android.view.MenuItem; import android.view.Window; import android.view.View; import android.view.Surface; +import android.graphics.Bitmap; +import android.graphics.Color; public class RenderScript { private static final String LOG_TAG = "libRS_jni"; @@ -79,6 +81,8 @@ public class RenderScript { native private int nAllocationCreateTyped(int type); native private int nAllocationCreatePredefSized(int predef, int count); native private int nAllocationCreateSized(int elem, int count); + native private int nAllocationCreateFromBitmap(int w, int h, int dstFmt, int srcFmt, boolean genMips, int[] data); + //native private int nAllocationCreateFromBitmap(type.mID); native private void nAllocationUploadToTexture(int alloc, int baseMioLevel); native private void nAllocationDestroy(int alloc); @@ -180,20 +184,21 @@ public class RenderScript { USER_I32 (5), USER_FLOAT (6), - RGB_565 (7), - RGBA_5551 (8), - RGBA_4444 (9), - RGB_888 (10), - RGBA_8888 (11), + A_8 (7), + RGB_565 (8), + RGBA_5551 (9), + RGBA_4444 (10), + RGB_888 (11), + RGBA_8888 (12), - INDEX_16 (12), - INDEX_32 (13), - XY_F32 (14), - XYZ_F32 (15), - ST_XY_F32 (16), - ST_XYZ_F32 (17), - NORM_XYZ_F32 (18), - NORM_ST_XYZ_F32 (19); + INDEX_16 (13), + INDEX_32 (14), + XY_F32 (15), + XYZ_F32 (16), + ST_XY_F32 (17), + ST_XYZ_F32 (18), + NORM_XYZ_F32 (19), + NORM_ST_XYZ_F32 (20); int mID; ElementPredefined(int id) { @@ -435,10 +440,44 @@ public class RenderScript { return new Allocation(id); } - //public Allocation allocationCreateFromBitmap(string file, boolean genMips) { - //int id = nAllocationCreateTyped(type.mID); - //return new Allocation(id); - //} + public Allocation allocationCreateFromBitmap(Bitmap b, ElementPredefined dstFmt, boolean genMips) { + int w = b.getWidth(); + int h = b.getHeight(); + int[] data = new int[w * h]; + + int outPtr = 0; + for(int y=0; y < h; y++) { + for(int x=0; x < w; x++) { + data[outPtr] = b.getPixel(x, y); + outPtr++; + } + } + + int srcFmt = 0; + /* + switch(b.getConfig()) { + case ALPHA_8: + srcFmt = ElementPredefined.A_8.mID; + break; + case ARGB_4444: + srcFmt = ElementPredefined.RGBA_4444.mID; + break; + case ARGB_8888: + srcFmt = ElementPredefined.RGBA_8888.mID; + break; + case RGB_565: + srcFmt = ElementPredefined.RGB_565.mID; + break; + default: + Log.e(LOG_TAG, "allocationCreateFromBitmap, unknown bitmap format"); + } + */ + + srcFmt = ElementPredefined.RGBA_8888.mID; + + int id = nAllocationCreateFromBitmap(w, h, dstFmt.mID, srcFmt, genMips, data); + return new Allocation(id); + } ////////////////////////////////////////////////////////////////////////////////// // Adapter1D diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp index b3444c0bb31b2..f3502e181f2ae 100644 --- a/libs/rs/jni/RenderScript_jni.cpp +++ b/libs/rs/jni/RenderScript_jni.cpp @@ -195,6 +195,10 @@ void test_script(void *con, const rsc_FunctionTable *ft, uint32_t launchID) } } + ft->contextBindProgramFragment(con, (RsProgramFragment)ft->loadEnvI32(con, 0, 7)); + ft->drawRect(con, 0, 256, 0, 512); + ft->contextBindProgramFragment(con, (RsProgramFragment)ft->loadEnvI32(con, 0, 6)); + if (touch) { int newPart = ft->loadEnvI32(con, 2, 0); for (int ct2=0; ct2GetIntField(_this, gContextId)); + jint len = _env->GetArrayLength(data); + LOG_API("nAllocationCreateFromBitmap, con(%p), w(%i), h(%i), dstFmt(%i), srcFmt(%i), mip(%i), len(%i)", con, w, h, dstFmt, srcFmt, genMips, len); + + jint *ptr = _env->GetIntArrayElements(data, NULL); + jint id = (jint)rsAllocationCreateFromBitmap(w, h, (RsElementPredefined)dstFmt, (RsElementPredefined)srcFmt, genMips, ptr); + _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT); + return id; +} + + + static void nAllocationDestroy(JNIEnv *_env, jobject _this, jint a) { @@ -939,7 +958,7 @@ static JNINativeMethod methods[] = { {"nAllocationCreateTyped", "(I)I", (void*)nAllocationCreateTyped }, {"nAllocationCreatePredefSized", "(II)I", (void*)nAllocationCreatePredefSized }, {"nAllocationCreateSized", "(II)I", (void*)nAllocationCreateSized }, -//{"nAllocationCreateFromBitmap", "(I)V", (void*)nAllocationCreateFromBitmap }, +{"nAllocationCreateFromBitmap", "(IIIIZ[I)I", (void*)nAllocationCreateFromBitmap }, {"nAllocationUploadToTexture", "(II)V", (void*)nAllocationUploadToTexture }, {"nAllocationDestroy", "(I)V", (void*)nAllocationDestroy }, {"nAllocationData", "(I[I)V", (void*)nAllocationData_i }, diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 6168bd75fab57..65a4a8261ef07 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -83,12 +83,23 @@ AllocationCreateSized { ret RsAllocation } -AllocationCreateFromBitmap { +AllocationCreateFromFile { param const char *file param bool genMips ret RsAllocation } +AllocationCreateFromBitmap { + param uint32_t width + param uint32_t height + param RsElementPredefined dstFmt + param RsElementPredefined srcFmt + param bool genMips + param const void * data + ret RsAllocation + } + + AllocationUploadToTexture { param RsAllocation alloc param uint32_t baseMipLevel diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index f3f2e4643bf93..7b8bc8034ff2d 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -213,7 +213,104 @@ static void mip(const Adapter2D &out, const Adapter2D &in) } -RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, const char *file, bool genMips) +typedef void (*ElementConverter_t)(void *dst, const void *src, uint32_t count); + +static void elementConverter_cpy_16(void *dst, const void *src, uint32_t count) +{ + memcpy(dst, src, count * 2); +} +static void elementConverter_cpy_8(void *dst, const void *src, uint32_t count) +{ + memcpy(dst, src, count); +} +static void elementConverter_cpy_32(void *dst, const void *src, uint32_t count) +{ + memcpy(dst, src, count * 4); +} + + +static void elementConverter_888_to_565(void *dst, const void *src, uint32_t count) +{ + uint16_t *d = static_cast(dst); + const uint8_t *s = static_cast(src); + + while(count--) { + *d = rs888to565(s[0], s[1], s[2]); + d++; + s+= 3; + } +} + +static void elementConverter_8888_to_565(void *dst, const void *src, uint32_t count) +{ + uint16_t *d = static_cast(dst); + const uint8_t *s = static_cast(src); + + while(count--) { + *d = rs888to565(s[0], s[1], s[2]); + d++; + s+= 4; + } +} + +static ElementConverter_t pickConverter(RsElementPredefined dstFmt, RsElementPredefined srcFmt) +{ + if ((dstFmt == RS_ELEMENT_RGB_565) && + (srcFmt == RS_ELEMENT_RGB_565)) { + return elementConverter_cpy_16; + } + + if ((dstFmt == RS_ELEMENT_RGB_565) && + (srcFmt == RS_ELEMENT_RGB_888)) { + return elementConverter_888_to_565; + } + + if ((dstFmt == RS_ELEMENT_RGB_565) && + (srcFmt == RS_ELEMENT_RGBA_8888)) { + return elementConverter_8888_to_565; + } + + + LOGE("pickConverter, unsuported combo"); + return 0; +} + + +RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt, bool genMips, const void *data) +{ + rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565)); + rsi_TypeAdd(rsc, RS_DIMENSION_X, w); + rsi_TypeAdd(rsc, RS_DIMENSION_Y, h); + if (genMips) { + rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1); + } + RsType type = rsi_TypeCreate(rsc); + + RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type); + Allocation *texAlloc = static_cast(vTexAlloc); + if (texAlloc == NULL) { + LOGE("Memory allocation failure"); + return NULL; + } + texAlloc->incRef(); + + ElementConverter_t cvt = pickConverter(dstFmt, srcFmt); + cvt(texAlloc->getPtr(), data, w * h); + + if (genMips) { + Adapter2D adapt(texAlloc); + Adapter2D adapt2(texAlloc); + for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) { + adapt.setLOD(lod); + adapt2.setLOD(lod + 1); + mip(adapt2, adapt); + } + } + + return texAlloc; +} + +RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips) { typedef struct _Win3xBitmapHeader { diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 163cf4df8c40e..abc5f456f8eaa 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -74,7 +74,7 @@ void Context::runRootScript() if(mRootScript->mIsOrtho) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrthof(0, 320, 0, 480, 0, 1); + glOrthof(0, 320, 480, 0, 0, 1); glMatrixMode(GL_MODELVIEW); } else { glMatrixMode(GL_PROJECTION); diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 16375440cb1d9..bd11f725d535a 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -91,6 +91,10 @@ void ElementState::initPredefined() e->setComponent(0, f_32); mPredefinedList.add(Predefined(RS_ELEMENT_USER_FLOAT, e)); + e = new Element(1); + e->setComponent(0, a_8); + mPredefinedList.add(Predefined(RS_ELEMENT_A_8, e)); + e = new Element(3); e->setComponent(0, r_5); e->setComponent(1, g_6); diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 5367c534d01ec..1a6e2c7fb51ac 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -40,7 +40,7 @@ void ProgramFragment::setupGL() for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) { glActiveTexture(GL_TEXTURE0 + ct); if (!(mTextureEnableMask & (1 << ct)) || - !mSamplers[ct].get() || + //!mSamplers[ct].get() || !mTextures[ct].get()) { glDisable(GL_TEXTURE_2D); @@ -62,7 +62,14 @@ void ProgramFragment::setupGL() break; } - mSamplers[ct]->setupGL(); +// if (mSamplers[ct].get()) { + //mSamplers[ct]->setupGL(); +// } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + //} } glActiveTexture(GL_TEXTURE0); } diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 3c4bfa2a59355..60339ecdc2d89 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -261,6 +261,35 @@ extern "C" void drawTriangleArray(void *vp, RsAllocation alloc, uint32_t count) glDrawArrays(GL_TRIANGLES, 0, count * 3); } +extern "C" void drawRect(void *vp, int32_t x1, int32_t x2, int32_t y1, int32_t y2) +{ + x1 = (x1 << 16); + x2 = (x2 << 16); + y1 = (y1 << 16); + y2 = (y2 << 16); + + int32_t vtx[] = {x1,y1, x1,y2, x2,y1, x2,y2}; + static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0, 0x10000,0x10000}; + + + ScriptC::Env * env = static_cast(vp); + env->mContext->setupCheck(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FIXED, 8, vtx); + glTexCoordPointer(2, GL_FIXED, 8, tex); + //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + extern "C" void pfBindTexture(void *vp, RsProgramFragment vpf, uint32_t slot, RsAllocation va) { //LOGE("pfBindTexture %p", vpf); @@ -326,8 +355,6 @@ static rsc_FunctionTable scriptCPtrTable = { matrixTranslate, color, - renderTriangleMesh, - renderTriangleMeshRange, pfBindTexture, pfBindSampler, @@ -341,9 +368,16 @@ static rsc_FunctionTable scriptCPtrTable = { disable, scriptRand, - drawTriangleArray, contextBindProgramFragment, - contextBindProgramFragmentStore + contextBindProgramFragmentStore, + + + renderTriangleMesh, + renderTriangleMeshRange, + + drawTriangleArray, + drawRect + }; From 982be3be054960fbf254f225b4aeaee0310e619f Mon Sep 17 00:00:00 2001 From: Jack Veenstra Date: Wed, 27 May 2009 15:07:59 -0700 Subject: [PATCH 03/43] Fix javadoc typos. No code changes. Also reformatted a comment that was too long. --- core/java/android/widget/TabHost.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index dc2c70de92e4b..5bf8035e8ccc5 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -87,8 +87,9 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode /** - *

Call setup() before adding tabs if loading TabHost using findViewById(). However: You do - * not need to call setup() after getTabHost() in {@link android.app.TabActivity TabActivity}. + *

Call setup() before adding tabs if loading TabHost using findViewById(). + * However: You do not need to call setup() after getTabHost() + * in {@link android.app.TabActivity TabActivity}. * Example:

mTabHost = (TabHost)findViewById(R.id.tabhost);
 mTabHost.setup();
@@ -363,14 +364,14 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
          * 
          * @param tag
          *            Which tab was selected.
-         * @return The view to distplay the contents of the selected tab.
+         * @return The view to display the contents of the selected tab.
          */
         View createTabContent(String tag);
     }
 
 
     /**
-     * A tab has a tab indictor, content, and a tag that is used to keep
+     * A tab has a tab indicator, content, and a tag that is used to keep
      * track of it.  This builder helps choose among these options.
      *
      * For the tab indicator, your choices are:
@@ -607,7 +608,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
             }
             mLaunchedView = wd;
             
-            // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activies for now so they can get
+            // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get
             // focus if none of their children have it. They need focus to be able to
             // display menu items.
             //

From 81d0a9a70ad68c836f89c5fcbebda95efe0048ab Mon Sep 17 00:00:00 2001
From: Jack Palevich 
Date: Wed, 27 May 2009 15:26:57 -0700
Subject: [PATCH 04/43] Enable renderscript to build under the simulator.

Added missing #include file to declare memcpy().
---
 libs/rs/rsMatrix.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp
index 7d22ae0095812..e68d5ac504395 100644
--- a/libs/rs/rsMatrix.cpp
+++ b/libs/rs/rsMatrix.cpp
@@ -17,6 +17,7 @@
 #include "rsMatrix.h"
 
 #include "stdlib.h"
+#include "string.h"
 #include "math.h"
 
 #include 

From c33fe6c9a71008d51aab7775532d73a3eaf12370 Mon Sep 17 00:00:00 2001
From: Amith Yamasani 
Date: Wed, 27 May 2009 15:29:04 -0700
Subject: [PATCH 05/43] Fix a hang during bootup.

Some data was not being written to the battery stats during shutdown. So there was insufficient
data when reading back at bootup.
---
 core/java/com/android/internal/os/BatteryStatsImpl.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a448ac6d4c692..51f3b025f84dc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -53,7 +53,7 @@ public final class BatteryStatsImpl extends BatteryStats {
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 36;
+    private static final int VERSION = 37;
 
     private final File mFile;
     private final File mBackupFile;
@@ -3014,6 +3014,8 @@ public final class BatteryStatsImpl extends BatteryStats {
             
             u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
             u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
             u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
             u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
 

From 7fea935590ad5629a1c8fe004f57ce79cf1c7a5a Mon Sep 17 00:00:00 2001
From: Andy McFadden 
Date: Wed, 27 May 2009 16:01:39 -0700
Subject: [PATCH 06/43] Sim-only files move, part 2/2.

Move Pipe and executablepath from libutils to the simulator, since nothing
else uses them.
---
 libs/utils/Android.mk                |   8 +-
 libs/utils/Pipe.cpp                  | 465 ---------------------------
 libs/utils/executablepath_darwin.cpp |  31 --
 libs/utils/executablepath_linux.cpp  |  30 --
 4 files changed, 1 insertion(+), 533 deletions(-)
 delete mode 100644 libs/utils/Pipe.cpp
 delete mode 100644 libs/utils/executablepath_darwin.cpp
 delete mode 100644 libs/utils/executablepath_linux.cpp

diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 30b6733decbe0..afad48e67f6bc 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -55,7 +55,6 @@ commonSources:= \
 #
 hostSources:= \
 	InetAddress.cpp \
-	Pipe.cpp \
 	Socket.cpp \
 	ZipEntry.cpp \
 	ZipFile.cpp
@@ -71,12 +70,7 @@ ifeq ($(HOST_OS),linux)
 # Use the futex based mutex and condition variable
 # implementation from android-arm because it's shared mem safe
 	LOCAL_SRC_FILES += \
-		futex_synchro.c \
-		executablepath_linux.cpp
-endif
-ifeq ($(HOST_OS),darwin)
-	LOCAL_SRC_FILES += \
-		executablepath_darwin.cpp
+		futex_synchro.c
 endif
 
 LOCAL_MODULE:= libutils
diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp
deleted file mode 100644
index 613906bedaa8f..0000000000000
--- a/libs/utils/Pipe.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Unidirectional pipe.
-//
-
-#include 
-#include 
-
-#if defined(HAVE_WIN32_IPC)
-# include 
-#else
-# include 
-# include 
-# include 
-#endif
-
-#include 
-#include 
-#include 
-#include 
-
-using namespace android;
-
-const unsigned long kInvalidHandle = (unsigned long) -1;
-
-
-/*
- * Constructor.  Do little.
- */
-Pipe::Pipe(void)
-    : mReadNonBlocking(false), mReadHandle(kInvalidHandle),
-      mWriteHandle(kInvalidHandle)
-{
-}
-
-/*
- * Destructor.  Use the system-appropriate close call.
- */
-Pipe::~Pipe(void)
-{
-#if defined(HAVE_WIN32_IPC)
-    if (mReadHandle != kInvalidHandle) {
-        if (!CloseHandle((HANDLE)mReadHandle))
-            LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n",
-                mReadHandle);
-    }
-    if (mWriteHandle != kInvalidHandle) {
-        FlushFileBuffers((HANDLE)mWriteHandle);
-        if (!CloseHandle((HANDLE)mWriteHandle))
-            LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n",
-                mWriteHandle);
-    }
-#else
-    if (mReadHandle != kInvalidHandle) {
-        if (close((int) mReadHandle) != 0)
-            LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n",
-                (int) mReadHandle);
-    }
-    if (mWriteHandle != kInvalidHandle) {
-        if (close((int) mWriteHandle) != 0)
-            LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n",
-                (int) mWriteHandle);
-    }
-#endif
-}
-
-/*
- * Create the pipe.
- *
- * Use the POSIX stuff for everything but Windows.
- */
-bool Pipe::create(void)
-{
-    assert(mReadHandle == kInvalidHandle);
-    assert(mWriteHandle == kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    /* we use this across processes, so they need to be inheritable */
-    HANDLE handles[2];
-    SECURITY_ATTRIBUTES saAttr;
-
-    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
-    saAttr.bInheritHandle = TRUE;
-    saAttr.lpSecurityDescriptor = NULL;
-
-    if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) {
-        LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
-        return false;
-    }
-    mReadHandle = (unsigned long) handles[0];
-    mWriteHandle = (unsigned long) handles[1];
-    return true;
-#else
-    int fds[2];
-
-    if (pipe(fds) != 0) {
-        LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
-        return false;
-    }
-    mReadHandle = fds[0];
-    mWriteHandle = fds[1];
-    return true;
-#endif
-}
-
-/*
- * Create a "half pipe".  Please, no Segway riding.
- */
-bool Pipe::createReader(unsigned long handle)
-{
-    mReadHandle = handle;
-    assert(mWriteHandle == kInvalidHandle);
-    return true;
-}
-
-/*
- * Create a "half pipe" for writing.
- */
-bool Pipe::createWriter(unsigned long handle)
-{
-    mWriteHandle = handle;
-    assert(mReadHandle == kInvalidHandle);
-    return true;
-}
-
-/*
- * Return "true" if create() has been called successfully.
- */
-bool Pipe::isCreated(void)
-{
-    // one or the other should be open
-    return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle);
-}
-
-
-/*
- * Read data from the pipe.
- *
- * For Linux and Darwin, just call read().  For Windows, implement
- * non-blocking reads by calling PeekNamedPipe first.
- */
-int Pipe::read(void* buf, int count)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    DWORD totalBytesAvail = count;
-    DWORD bytesRead;
-
-    if (mReadNonBlocking) {
-        // use PeekNamedPipe to adjust read count expectations
-        if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
-                &totalBytesAvail, NULL))
-        {
-            LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
-            return -1;
-        }
-
-        if (totalBytesAvail == 0)
-            return 0;
-    }
-
-    if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead,
-            NULL))
-    {
-        DWORD err = GetLastError();
-        if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
-            return 0;
-        LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err);
-        return -1;
-    }
-
-    return (int) bytesRead;
-#else
-    int cc;
-    cc = ::read(mReadHandle, buf, count);
-    if (cc < 0 && errno == EAGAIN)
-        return 0;
-    return cc;
-#endif
-}
-
-/*
- * Write data to the pipe.
- *
- * POSIX systems are trivial, Windows uses a different call and doesn't
- * handle non-blocking writes.
- *
- * If we add non-blocking support here, we probably want to make it an
- * all-or-nothing write.
- *
- * DO NOT use LOG() here, we could be writing a log message.
- */
-int Pipe::write(const void* buf, int count)
-{
-    assert(mWriteHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    DWORD bytesWritten;
-
-    if (mWriteNonBlocking) {
-        // BUG: can't use PeekNamedPipe() to get the amount of space
-        // left.  Looks like we need to use "overlapped I/O" functions.
-        // I just don't care that much.
-    }
-
-    if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) {
-        // can't LOG, use stderr
-        fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError());
-        return -1;
-    }
-
-    return (int) bytesWritten;
-#else
-    int cc;
-    cc = ::write(mWriteHandle, buf, count);
-    if (cc < 0 && errno == EAGAIN)
-        return 0;
-    return cc;
-#endif
-}
-
-/*
- * Figure out if there is data available on the read fd.
- *
- * We return "true" on error because we want the caller to try to read
- * from the pipe.  They'll notice the read failure and do something
- * appropriate.
- */
-bool Pipe::readReady(void)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    DWORD totalBytesAvail;
-
-    if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
-            &totalBytesAvail, NULL))
-    {
-        LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
-        return true;
-    }
-
-    return (totalBytesAvail != 0);
-#else
-    errno = 0;
-    fd_set readfds;
-    struct timeval tv = { 0, 0 };
-    int cc;
-
-    FD_ZERO(&readfds);
-    FD_SET(mReadHandle, &readfds);
-
-    cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv);
-    if (cc < 0) {
-        LOG(LOG_ERROR, "pipe", "select() failed\n");
-        return true;
-    } else if (cc == 0) {
-        /* timed out, nothing available */
-        return false;
-    } else if (cc == 1) {
-        /* our fd is ready */
-        return true;
-    } else {
-        LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n");
-        return true;
-    }
-#endif
-}
-
-/*
- * Enable or disable non-blocking mode for the read descriptor.
- *
- * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to
- * actually be in non-blocking mode.  If this matters -- i.e. you're not
- * using a select() call -- put a call to readReady() in front of the
- * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for
- * Darwin.
- */
-bool Pipe::setReadNonBlocking(bool val)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    // nothing to do
-#else
-    int flags;
-
-    if (fcntl(mReadHandle, F_GETFL, &flags) == -1) {
-        LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n");
-        return false;
-    }
-    if (val)
-        flags |= O_NONBLOCK;
-    else
-        flags &= ~(O_NONBLOCK);
-    if (fcntl(mReadHandle, F_SETFL, &flags) == -1) {
-        LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n");
-        return false;
-    }
-#endif
-
-    mReadNonBlocking = val;
-    return true;
-}
-
-/*
- * Enable or disable non-blocking mode for the write descriptor.
- *
- * As with setReadNonBlocking(), this does not work on the Mac.
- */
-bool Pipe::setWriteNonBlocking(bool val)
-{
-    assert(mWriteHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
-    // nothing to do
-#else
-    int flags;
-
-    if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) {
-        LOG(LOG_WARN, "pipe",
-            "Warning: couldn't get flags for pipe write fd (errno=%d)\n",
-            errno);
-        return false;
-    }
-    if (val)
-        flags |= O_NONBLOCK;
-    else
-        flags &= ~(O_NONBLOCK);
-    if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) {
-        LOG(LOG_WARN, "pipe",
-            "Warning: couldn't set flags for pipe write fd (errno=%d)\n",
-            errno);
-        return false;
-    }
-#endif
-
-    mWriteNonBlocking = val;
-    return true;
-}
-
-/*
- * Specify whether a file descriptor can be inherited by a child process.
- * Under Linux this means setting the close-on-exec flag, under Windows
- * this is SetHandleInformation(HANDLE_FLAG_INHERIT).
- */
-bool Pipe::disallowReadInherit(void)
-{
-    if (mReadHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0)
-        return false;
-#else
-    if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0)
-        return false;
-#endif
-    return true;
-}
-bool Pipe::disallowWriteInherit(void)
-{
-    if (mWriteHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0)
-        return false;
-#else
-    if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0)
-        return false;
-#endif
-    return true;
-}
-
-/*
- * Close read descriptor.
- */
-bool Pipe::closeRead(void)
-{
-    if (mReadHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (mReadHandle != kInvalidHandle) {
-        if (!CloseHandle((HANDLE)mReadHandle)) {
-            LOG(LOG_WARN, "pipe", "failed closing read handle\n");
-            return false;
-        }
-    }
-#else
-    if (mReadHandle != kInvalidHandle) {
-        if (close((int) mReadHandle) != 0) {
-            LOG(LOG_WARN, "pipe", "failed closing read fd\n");
-            return false;
-        }
-    }
-#endif
-    mReadHandle = kInvalidHandle;
-    return true;
-}
-
-/*
- * Close write descriptor.
- */
-bool Pipe::closeWrite(void)
-{
-    if (mWriteHandle == kInvalidHandle)
-        return false;
-
-#if defined(HAVE_WIN32_IPC)
-    if (mWriteHandle != kInvalidHandle) {
-        if (!CloseHandle((HANDLE)mWriteHandle)) {
-            LOG(LOG_WARN, "pipe", "failed closing write handle\n");
-            return false;
-        }
-    }
-#else
-    if (mWriteHandle != kInvalidHandle) {
-        if (close((int) mWriteHandle) != 0) {
-            LOG(LOG_WARN, "pipe", "failed closing write fd\n");
-            return false;
-        }
-    }
-#endif
-    mWriteHandle = kInvalidHandle;
-    return true;
-}
-
-/*
- * Get the read handle.
- */
-unsigned long Pipe::getReadHandle(void)
-{
-    assert(mReadHandle != kInvalidHandle);
-
-    return mReadHandle;
-}
-
-/*
- * Get the write handle.
- */
-unsigned long Pipe::getWriteHandle(void)
-{
-    assert(mWriteHandle != kInvalidHandle);
-
-    return mWriteHandle;
-}
-
diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp
deleted file mode 100644
index 2e3c3a01f5df7..0000000000000
--- a/libs/utils/executablepath_darwin.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include 
-#import 
-#include 
-
-void executablepath(char s[PATH_MAX])
-{
-    ProcessSerialNumber psn;
-    GetCurrentProcess(&psn);
-    CFDictionaryRef dict;
-    dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
-    CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
-                CFSTR("CFBundleExecutable"));
-    CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8);
-}
-
diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp
deleted file mode 100644
index b8d2a3d6fbc97..0000000000000
--- a/libs/utils/executablepath_linux.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include 
-#include 
-#include 
-#include 
-#include 
-
-void executablepath(char exe[PATH_MAX])
-{
-    char proc[100];
-    sprintf(proc, "/proc/%d/exe", getpid());
-    
-    int err = readlink(proc, exe, PATH_MAX);
-}
-

From df98851d2fccc00d7bc9adfdf1088d34e007ae1a Mon Sep 17 00:00:00 2001
From: Jack Palevich 
Date: Wed, 27 May 2009 17:00:45 -0700
Subject: [PATCH 07/43] Change the Fountain test app's package to
 com.android.fountain.

It had been com.android.calc, which caused conflicts with the real Calc
application.
---
 libs/rs/java/Fountain/AndroidManifest.xml                       | 2 +-
 libs/rs/java/Fountain/src/com/android/fountain/Fountain.java    | 2 +-
 .../rs/java/Fountain/src/com/android/fountain/FountainView.java | 2 +-
 .../java/Fountain/src/com/android/fountain/RSSurfaceView.java   | 2 +-
 .../rs/java/Fountain/src/com/android/fountain/RenderScript.java | 2 +-
 libs/rs/jni/RenderScript_jni.cpp                                | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml
index 62b0b0d1780d3..a10938bae863d 100644
--- a/libs/rs/java/Fountain/AndroidManifest.xml
+++ b/libs/rs/java/Fountain/AndroidManifest.xml
@@ -1,6 +1,6 @@
 
 
+    package="com.android.fountain">
     
         
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
index ca0f02003ccc6..0d669660c84b0 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.calc;
+package com.android.fountain;
 
 import android.app.Activity;
 import android.content.res.Configuration;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
index b6aa9f8bab1ba..eb848a570f237 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.calc;
+package com.android.fountain;
 
 import java.io.Writer;
 import java.util.ArrayList;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RSSurfaceView.java b/libs/rs/java/Fountain/src/com/android/fountain/RSSurfaceView.java
index d18270a70b191..a8b3bca95f480 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/RSSurfaceView.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/RSSurfaceView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.calc;
+package com.android.fountain;
 
 import java.io.Writer;
 import java.util.ArrayList;
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
index ffde1a268b3f6..a62605377c18a 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.calc;
+package com.android.fountain;
 
 import android.os.Bundle;
 import android.util.Log;
diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp
index f3502e181f2ae..2a199c35aa7ef 100644
--- a/libs/rs/jni/RenderScript_jni.cpp
+++ b/libs/rs/jni/RenderScript_jni.cpp
@@ -934,7 +934,7 @@ nContextBindProgramFragment(JNIEnv *_env, jobject _this, jint pf)
 // ---------------------------------------------------------------------------
 
 
-static const char *classPathName = "com/android/calc/RenderScript";
+static const char *classPathName = "com/android/fountain/RenderScript";
 
 static JNINativeMethod methods[] = {
 {"_nInit",                         "()V",                                  (void*)_nInit },

From 0ae234c991f46a4fd6c9abb44edba7af89569718 Mon Sep 17 00:00:00 2001
From: Jason Sams 
Date: Wed, 27 May 2009 17:46:57 -0700
Subject: [PATCH 08/43] Add background bitmap to fountain test app for
 renderscript.

---
 .../res/drawable/gadgets_clock_mp3.png        | Bin 0 -> 104862 bytes
 .../com/android/fountain/FountainView.java    |  29 +++++++++++++++++-
 libs/rs/jni/RenderScript_jni.cpp              |   4 +++
 3 files changed, 32 insertions(+), 1 deletion(-)
 create mode 100755 libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png

diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
new file mode 100755
index 0000000000000000000000000000000000000000..e91bfb418a8e0c10d0e69b950e0ee39303a4a0b8
GIT binary patch
literal 104862
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%0D{YnKY=ebH}QO2)5b
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
Date: Wed, 27 May 2009 18:19:04 -0700
Subject: [PATCH 09/43] Bug fixes and performance improvements

- Added affine transform functions in GestureUtilities to remove Matrix
- Fixed a bug with Instance.createInstance
- Updated letter recognition file
---
 core/java/android/gesture/GestureLibrary.java |  10 ++--
 core/java/android/gesture/GestureStroke.java  |  11 +++--
 .../android/gesture/GestureUtilities.java     |  44 ++++++++++++++----
 core/java/android/gesture/Instance.java       |  32 ++++++-------
 .../java/android/gesture/InstanceLearner.java |   9 +---
 core/res/res/raw/latin_lowercase              | Bin 28494 -> 28496 bytes
 6 files changed, 65 insertions(+), 41 deletions(-)

diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java
index 1cf192ec34461..9020d2bafc056 100644
--- a/core/java/android/gesture/GestureLibrary.java
+++ b/core/java/android/gesture/GestureLibrary.java
@@ -136,7 +136,7 @@ public class GestureLibrary {
      * @return a list of predictions of possible entries for a given gesture
      */
     public ArrayList recognize(Gesture gesture) {
-        Instance instance = Instance.createInstance(mSequenceType, gesture, null);
+        Instance instance = Instance.createInstance(mSequenceType, mOrientationStyle, gesture, null);
         return mClassifier.classify(mSequenceType, instance.vector);
     }
 
@@ -156,7 +156,7 @@ public class GestureLibrary {
             mNamedGestures.put(entryName, gestures);
         }
         gestures.add(gesture);
-        mClassifier.addInstance(Instance.createInstance(mSequenceType, gesture, entryName));
+        mClassifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName));
         mChanged = true;
     }
 
@@ -337,10 +337,14 @@ public class GestureLibrary {
             for (int j = 0; j < gestureCount; j++) {
                 final Gesture gesture = Gesture.deserialize(in);
                 gestures.add(gesture);
-                classifier.addInstance(Instance.createInstance(mSequenceType, gesture, name));
+                classifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name));
             }
 
             namedGestures.put(name, gestures);
         }
     }
+    
+    Learner getLearner() {
+        return mClassifier;
+    }
 }
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 0d7bc2d049d4e..90faaed6430f4 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -17,7 +17,6 @@
 package android.gesture;
 
 import android.graphics.Canvas;
-import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.RectF;
@@ -147,10 +146,12 @@ public class GestureStroke {
         final float[] pts = GestureUtilities.temporalSampling(this, numSample);
         final RectF rect = boundingBox;
 
-        final Matrix matrix = new Matrix();
-        matrix.setTranslate(-rect.left, -rect.top);
-        matrix.postScale(width / rect.width(), height / rect.height());
-        matrix.mapPoints(pts);
+        GestureUtilities.translate(pts, -rect.left, -rect.top);
+        
+        float sx = width / rect.width();
+        float sy = height / rect.height();
+        float scale = sx > sy ? sy : sx;
+        GestureUtilities.scale(pts, scale, scale);
 
         float mX = 0;
         float mY = 0;
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index 4a3144c2f0b74..3e8a3c836712c 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -17,7 +17,6 @@
 package android.gesture;
 
 import android.graphics.RectF;
-import android.graphics.Matrix;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -380,22 +379,17 @@ final class GestureUtilities {
     }
 
     static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) {
-        Matrix tr = new Matrix();
-        tr.setTranslate(-centroid[0], -centroid[1]);
-        tr.mapPoints(points);
+        translate(points, -centroid[0], -centroid[1]);
 
         double[][] array = computeCoVariance(points);
         double[] targetVector = computeOrientation(array);
 
         float angle;
         if (targetVector[0] == 0 && targetVector[1] == 0) {
-            angle = -90;
+            angle = (float) -Math.PI/2;
         } else { // -PI classify(int gestureType, float[] vector) {
+    ArrayList classify(int sequenceType, float[] vector) {
         ArrayList predictions = new ArrayList();
         ArrayList instances = getInstances();
         int count = instances.size();
@@ -43,7 +38,7 @@ class InstanceLearner extends Learner {
                 continue;
             }
             double distance;
-            if (gestureType == GestureLibrary.SEQUENCE_SENSITIVE) {
+            if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) {
                 distance = GestureUtilities.cosineDistance(sample.vector, vector);
             } else {
                 distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
diff --git a/core/res/res/raw/latin_lowercase b/core/res/res/raw/latin_lowercase
index 17cfaf00bf3e2101b74078dc4839a5668e37d96b..443d5f8ecfebcde147680bab06ba416a0e1c3736 100644
GIT binary patch
literal 28496
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

literal 28494
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�J
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
KTh*L0XWjoupS1=6


From b0ded43d3844d9d97c5d554e8ca4ec3ee2c7bbe1 Mon Sep 17 00:00:00 2001
From: Romain Guy 
Date: Wed, 27 May 2009 21:37:14 -0700
Subject: [PATCH 10/43] Uninstalls the gestures overlay when the letters
 recognizer cannot be loaded instead of simply dismissing the popup. This is
 cleaner and reuses the setGestures() method.

---
 core/java/android/widget/AbsListView.java | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3c827a0bad9cd..20e2c4688677b 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3865,8 +3865,7 @@ public abstract class AbsListView extends AdapterView implements Te
             mRecognizer = LetterRecognizer.getLetterRecognizer(getContext(),
                     LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE);
             if (mRecognizer == null) {
-                dismissGesturesPopup();
-                mGestures = GESTURES_NONE;
+                setGestures(GESTURES_NONE);
             }
             if (mGestures == GESTURES_FILTER) {
                 mKeyMap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);

From 2542c0a805b144b4c4324a749574d9ba76660557 Mon Sep 17 00:00:00 2001
From: Dirk Dougherty 
Date: Thu, 28 May 2009 09:44:39 -0700
Subject: [PATCH 11/43] AI 149346: Replace icon_templates-v1.0.zip with new
 archive from cnesladek. remove mac-specific files, fix dir name, repack.  
 BUG=1877969

Automated import of CL 149346
---
 docs/html/shareables/icon_templates-v1.0.zip | Bin 3994006 -> 4001936 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/docs/html/shareables/icon_templates-v1.0.zip b/docs/html/shareables/icon_templates-v1.0.zip
index 3e64f9aacd50ce4eb668c7af0386e25b374cfeeb..94fbcdcea6cc4bb914a9536eb8ef25e17e260b93 100644
GIT binary patch
delta 22830
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

delta 14674
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


From 14f861a24af316ec6d60ef8f17c954b50a7f6fec Mon Sep 17 00:00:00 2001
From: Jean-Michel Trivi 
Date: Thu, 28 May 2009 11:11:25 -0700
Subject: [PATCH 12/43] Adding TTS error code to signal missing resources (for
 instance missing language files).

---
 include/tts/TtsEngine.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 06f3820030555..bf629958ba78c 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -53,7 +53,8 @@ enum tts_result {
     TTS_FEATURE_UNSUPPORTED     = -2,
     TTS_VALUE_INVALID           = -3,
     TTS_PROPERTY_UNSUPPORTED    = -4,
-    TTS_PROPERTY_SIZE_TOO_SMALL = -5
+    TTS_PROPERTY_SIZE_TOO_SMALL = -5,
+    TTS_MISSING_RESOURCES       = -6
 };
 
 class TtsEngine

From 436466d75edb5f6fd848504d998f244426ea5a09 Mon Sep 17 00:00:00 2001
From: Romain Guy 
Date: Thu, 28 May 2009 13:24:43 -0700
Subject: [PATCH 13/43] Revert "Bug fixes and performance improvements"

This reverts commit 58b359041a29418876f12d37a7082ece9f8a38a4.
---
 core/java/android/gesture/GestureLibrary.java | 10 ++---
 core/java/android/gesture/GestureStroke.java  | 11 +++--
 .../android/gesture/GestureUtilities.java     | 44 ++++---------------
 core/java/android/gesture/Instance.java       | 32 +++++++-------
 .../java/android/gesture/InstanceLearner.java |  9 +++-
 5 files changed, 41 insertions(+), 65 deletions(-)

diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java
index 9020d2bafc056..1cf192ec34461 100644
--- a/core/java/android/gesture/GestureLibrary.java
+++ b/core/java/android/gesture/GestureLibrary.java
@@ -136,7 +136,7 @@ public class GestureLibrary {
      * @return a list of predictions of possible entries for a given gesture
      */
     public ArrayList recognize(Gesture gesture) {
-        Instance instance = Instance.createInstance(mSequenceType, mOrientationStyle, gesture, null);
+        Instance instance = Instance.createInstance(mSequenceType, gesture, null);
         return mClassifier.classify(mSequenceType, instance.vector);
     }
 
@@ -156,7 +156,7 @@ public class GestureLibrary {
             mNamedGestures.put(entryName, gestures);
         }
         gestures.add(gesture);
-        mClassifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName));
+        mClassifier.addInstance(Instance.createInstance(mSequenceType, gesture, entryName));
         mChanged = true;
     }
 
@@ -337,14 +337,10 @@ public class GestureLibrary {
             for (int j = 0; j < gestureCount; j++) {
                 final Gesture gesture = Gesture.deserialize(in);
                 gestures.add(gesture);
-                classifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name));
+                classifier.addInstance(Instance.createInstance(mSequenceType, gesture, name));
             }
 
             namedGestures.put(name, gestures);
         }
     }
-    
-    Learner getLearner() {
-        return mClassifier;
-    }
 }
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index 90faaed6430f4..0d7bc2d049d4e 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -17,6 +17,7 @@
 package android.gesture;
 
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.RectF;
@@ -146,12 +147,10 @@ public class GestureStroke {
         final float[] pts = GestureUtilities.temporalSampling(this, numSample);
         final RectF rect = boundingBox;
 
-        GestureUtilities.translate(pts, -rect.left, -rect.top);
-        
-        float sx = width / rect.width();
-        float sy = height / rect.height();
-        float scale = sx > sy ? sy : sx;
-        GestureUtilities.scale(pts, scale, scale);
+        final Matrix matrix = new Matrix();
+        matrix.setTranslate(-rect.left, -rect.top);
+        matrix.postScale(width / rect.width(), height / rect.height());
+        matrix.mapPoints(pts);
 
         float mX = 0;
         float mY = 0;
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java
index 3e8a3c836712c..4a3144c2f0b74 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtilities.java
@@ -17,6 +17,7 @@
 package android.gesture;
 
 import android.graphics.RectF;
+import android.graphics.Matrix;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -379,17 +380,22 @@ final class GestureUtilities {
     }
 
     static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) {
-        translate(points, -centroid[0], -centroid[1]);
+        Matrix tr = new Matrix();
+        tr.setTranslate(-centroid[0], -centroid[1]);
+        tr.mapPoints(points);
 
         double[][] array = computeCoVariance(points);
         double[] targetVector = computeOrientation(array);
 
         float angle;
         if (targetVector[0] == 0 && targetVector[1] == 0) {
-            angle = (float) -Math.PI/2;
+            angle = -90;
         } else { // -PI classify(int sequenceType, float[] vector) {
+    ArrayList classify(int gestureType, float[] vector) {
         ArrayList predictions = new ArrayList();
         ArrayList instances = getInstances();
         int count = instances.size();
@@ -38,7 +43,7 @@ class InstanceLearner extends Learner {
                 continue;
             }
             double distance;
-            if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) {
+            if (gestureType == GestureLibrary.SEQUENCE_SENSITIVE) {
                 distance = GestureUtilities.cosineDistance(sample.vector, vector);
             } else {
                 distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);

From fc1b15cfbfc5f69235ec1f367abd7a909381cd05 Mon Sep 17 00:00:00 2001
From: Robert Greenwalt 
Date: Fri, 22 May 2009 15:09:51 -0700
Subject: [PATCH 14/43] Fix wifi multicast API for public use.

Applying API review comments and taking it public.
---
 api/current.xml                               |  66 ++++++++
 core/res/AndroidManifest.xml                  |   7 +
 core/res/res/values/strings.xml               |   9 +
 .../java/com/android/server/WifiService.java  |  14 +-
 wifi/java/android/net/wifi/IWifiManager.aidl  |   4 +-
 wifi/java/android/net/wifi/WifiManager.java   | 157 +++++++++++++-----
 .../android/net/wifi/WifiStateTracker.java    |   6 +-
 7 files changed, 209 insertions(+), 54 deletions(-)

diff --git a/api/current.xml b/api/current.xml
index ce1b7269428bc..bf3044e30fce3 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -309,6 +309,17 @@
  visibility="public"
 >
 
+
+
 
 
 
+
+
+
+
 
 
 
+
+
+
+
+
+
+
+
 
 
+    
+    
+
     
     
 
+    
+    allow Wi-Fi Multicast
+      reception
+    
+    Allows an application to
+      receive packets not directly addressed to your device.  This can be
+      useful when discovering services offered near by.  It uses more power
+      than the non-multicast mode.
+
     
     bluetooth administration
     
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5fa8701fda050..1528ba34efc72 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -588,6 +588,12 @@ public class WifiService extends IWifiManager.Stub {
 
     }
 
+    private void enforceMulticastChangePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
+                "WifiService");
+    }
+
     /**
      * see {@link WifiManager#getWifiState()}
      * @return One of {@link WifiManager#WIFI_STATE_DISABLED},
@@ -1930,8 +1936,8 @@ public class WifiService extends IWifiManager.Stub {
         }
     }
 
-    public void enableMulticast(IBinder binder, String tag) {
-        enforceChangePermission();
+    public void acquireMulticastLock(IBinder binder, String tag) {
+        enforceMulticastChangePermission();
 
         synchronized (mMulticasters) {
             mMulticastEnabled++;
@@ -1953,8 +1959,8 @@ public class WifiService extends IWifiManager.Stub {
         }
     }
 
-    public void disableMulticast() {
-        enforceChangePermission();
+    public void releaseMulticastLock() {
+        enforceMulticastChangePermission();
 
         int uid = Binder.getCallingUid();
         synchronized (mMulticasters) {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 7c3af69341e5b..3d65d3c66f388 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -72,8 +72,8 @@ interface IWifiManager
 
     boolean isMulticastEnabled();
 
-    void enableMulticast(IBinder binder, String tag);
+    void acquireMulticastLock(IBinder binder, String tag);
 
-    void disableMulticast();
+    void releaseMulticastLock();
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1a7caef187983..5b8ced67662cf 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -824,6 +824,117 @@ public class WifiManager {
         return new WifiLock(WIFI_MODE_FULL, tag);
     }
 
+
+    /**
+     * Create a new MulticastLock
+     *
+     * @param tag a tag for the MulticastLock to identify it in debugging
+     *            messages.
+     *
+     * @return a new, unacquired MulticastLock with the given tag.
+     *
+     * @see MulticastLock
+     */
+    public MulticastLock createMulticastLock(String tag) {
+        return new MulticastLock(tag);
+    }
+
+    /**
+     * Allows an application to receive Wifi Multicast packets.
+     * Normally the Wifi stack filters out packets not explicitly
+     * addressed to this device.  Acquring a MulticastLock will
+     * cause the stack to receive packets addressed to multicast
+     * addresses.  Processing these extra packets can cause a noticable
+     * battery drain and should be disabled when not needed
+     */
+    public class MulticastLock {
+        private String mTag;
+        private final IBinder mBinder;
+        private boolean mHeld;
+
+        private MulticastLock(String tag) {
+            mTag = tag;
+            mBinder = new Binder();
+            mHeld = false;
+        }
+
+        /**
+         * Locks Wifi Multicast on until {@link #release} is called.
+         *
+         * The first call to {@code acquire} will lock the Multicast on
+         * but subsequent calls will be ignored.  Only one call to
+         * {@link #release} will be required, regardless of the number of
+         * times that {@code acquire} is called.
+         *
+         * Note that other applications may also lock Wifi Multicast on.
+         * Only they can relinquish their lock.
+         *
+         * Also note that applications cannot leave Multicast locked on.
+         * When an app exits or crashes, any Multicast locks will be released.
+         */
+        public void acquire() {
+            synchronized (mBinder) {
+                if (!mHeld) {
+                    try {
+                        mService.acquireMulticastLock(mBinder, mTag);
+                        mHeld = true;
+                    } catch (RemoteException ignore) {
+                    }
+                }
+            }
+        }
+
+        /**
+         * Unlocks Wifi Multicast, restoring the filter of packets
+         * not addressed specifically to this device and saving power.
+         *
+         * Note that if any other Wifi Multicast Locks are still outstanding
+         * this {@code release} call will not have an immediate effect.  Only
+         * when all applications have released all their Multicast Locks will
+         * the Multicast filter be turned back on.
+         *
+         * Also note that when an app exits or crashes all of its Multicast
+         * Locks will be automatically released.
+         */
+        public void release() {
+            synchronized (mBinder) {
+                if (mHeld) {
+                    try {
+                        mService.releaseMulticastLock();
+                        mHeld = false;
+                    } catch (RemoteException ignore) {
+                    }
+                }
+            }
+        }
+
+        /**
+         * Checks whether this MulticastLock is currently held.
+         *
+         * @return true if this MulticastLock is held, false otherwise
+         */
+        public boolean isHeld() {
+            synchronized (mBinder) {
+                return mHeld;
+            }
+        }
+
+        public String toString() {
+            String s1, s2;
+            synchronized (mBinder) {
+                s1 = Integer.toHexString(System.identityHashCode(this));
+                s2 = mHeld ? "held; " : "";
+                return "MulticastLock{ " + s1 + "; " + s2 + " }";
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            release();
+        }
+    }
+
     /**
      * Check multicast filter status.
      *
@@ -838,50 +949,4 @@ public class WifiManager {
             return false;
         }
     }
-
-    /**
-     * Turn on the reception of multicast packets.
-     * The default behavior is to disable multicast packets as they
-     * have a noticable negative effect on battery life.  An
-     * application can turn them on, but should not leave it on for longer
-     * than needed.  When the app quits (or crashes) its request will
-     * be reverted.
-     *
-     * @param tag a string associated with this request for debugging.
-     *
-     * @return true on success
-     *
-     * @see #disableMulticast
-     *
-     * @hide pending API council approval
-     */
-    public boolean enableMulticast(String tag) {
-        try {
-            mService.enableMulticast(new Binder(), tag);
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
-
-    /**
-     * Return to the default multicast-off setting.
-     * Note that if others had turned on Multicast reception, your
-     * call will not turn it back off - they must also turn off their
-     * request for multicast reception.
-     *
-     * @return true on success
-     *
-     * @see #enableMulticast
-     *
-     * @hide pending API council approval
-     */
-    public boolean disableMulticast() {
-        try {
-            mService.disableMulticast();
-            return true;
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
 }
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 6771136ee3793..2fbc779503589 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -747,8 +747,10 @@ public class WifiStateTracker extends NetworkStateTracker {
                  * first and then off.. if nobody else wants it on it'll be
                  * off then and it's all synchronized within the API.
                  */
-                mWM.enableMulticast("WifiStateTracker");
-                mWM.disableMulticast();
+                WifiManager.MulticastLock l = 
+                        mWM.createMulticastLock("WifiStateTracker");
+                l.acquire();
+                l.release();
 
                 if (mBluetoothA2dp == null) {
                     mBluetoothA2dp = new BluetoothA2dp(mContext);

From 43702d8925c54360ad5f9f66b0d35d61d59f6910 Mon Sep 17 00:00:00 2001
From: Jack Palevich 
Date: Thu, 28 May 2009 13:38:16 -0700
Subject: [PATCH 15/43] Add support for setting scripts, rather than having a
 hard-coded script.

Move the test script into a resource file.
Add APIs for reading a script from a resource, InputStream, string, or
byte array.
---
 libs/rs/java/Fountain/res/raw/fountain.c      | 111 +++++++
 .../com/android/fountain/FountainView.java    |   2 +-
 .../com/android/fountain/RenderScript.java    |  63 +++-
 libs/rs/jni/RenderScript_jni.cpp              | 298 +++---------------
 4 files changed, 219 insertions(+), 255 deletions(-)
 create mode 100644 libs/rs/java/Fountain/res/raw/fountain.c

diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c
new file mode 100644
index 0000000000000..76f8dcf10ce14
--- /dev/null
+++ b/libs/rs/java/Fountain/res/raw/fountain.c
@@ -0,0 +1,111 @@
+// Fountain test script
+
+main(con, ft, launchID) {
+    int count, touch, x, y, rate, maxLife, lifeShift;
+    int life;
+    int ct, ct2;
+    int newPart;
+    int drawCount;
+    int dx, dy, idx;
+    int partPtr;
+    int vertPtr;
+    int posx,posy;
+    int c;
+
+    count = loadI32(con, 0, 1);
+    touch = loadI32(con, 0, 2);
+    x = loadI32(con, 0, 3);
+    y = 480 - loadI32(con, 0, 4);
+
+    rate = 4;
+    maxLife = (count / rate) - 1;
+    lifeShift = 0;
+    {
+        life = maxLife;
+        while (life > 255) {
+            life = life >> 1;
+            lifeShift ++;
+        }
+    }
+
+    contextBindProgramFragment(con, loadI32(con, 0, 7));
+    drawRect(con, 0, 256, 0, 512);
+    contextBindProgramFragment(con, loadI32(con, 0, 6));
+
+    if (touch) {
+        newPart = loadI32(con, 2, 0);
+        for (ct2=0; ct2= count) {
+                newPart = 0;
+            }
+        }
+        storeI32(con, 2, 0, newPart);
+    }
+
+    // Emulate intrinsic perf...
+    partPtr = loadVp(con, 2, 4);
+    vertPtr = loadVp(con, 1, 0);
+
+    drawCount = 0;
+    for (ct=0; ct < count; ct++) {
+        //int srcIdx = ct * 5 + 1;
+        //int dstIdx = ct * 3 * 3;
+
+        dx = * (int* )(partPtr + 0); //loadEnvI32(con, 2, srcIdx);
+        dy = * (int* )(partPtr + 4); //loadEnvI32(con, 2, srcIdx + 1);
+        life = * (int* )(partPtr + 8); //loadEnvI32(con, 2, srcIdx + 2);
+        posx = * (int* )(partPtr + 12); //loadEnvI32(con, 2, srcIdx + 3);
+        posy = * (int* )(partPtr + 16); //loadEnvI32(con, 2, srcIdx + 4);
+
+        if (life) {
+            if (posy > 0) {
+                c = 0xffafcf | ((life >> lifeShift) << 24);
+
+                * (int* )(vertPtr) = c; //storeEnvU32(con, 1, dstIdx, c);
+                * (int* )(vertPtr + 4) = posx; //storeEnvI32(con, 1, dstIdx + 1, posx);
+                * (int* )(vertPtr + 8) = posy; //storeEnvI32(con, 1, dstIdx + 2, posy);
+
+                * (int* )(vertPtr + 12) = c; //storeEnvU32(con, 1, dstIdx + 3, c);
+                * (int* )(vertPtr + 16) = posx + 0x10000; //storeEnvI32(con, 1, dstIdx + 4, posx + 0x10000);
+                * (int* )(vertPtr + 20) = posy + dy * 4; //storeEnvI32(con, 1, dstIdx + 5, posy);
+
+                * (int* )(vertPtr + 24) = c; //storeEnvU32(con, 1, dstIdx + 6, c);
+                * (int* )(vertPtr + 28) = posx - 0x10000; //storeEnvI32(con, 1, dstIdx + 7, posx + 0x0800);
+                * (int* )(vertPtr + 32) = posy + dy * 4; //storeEnvI32(con, 1, dstIdx + 8, posy + 0x10000);
+
+                vertPtr = vertPtr + 36;
+                drawCount ++;
+            } else {
+                if (dy < 0) {
+                    dy = (-dy) >> 1;
+                }
+            }
+
+            posx = posx + dx;
+            posy = posy + dy;
+            dy = dy - 0x400;
+            life --;
+
+            * (int* )(partPtr + 0) = dx; //storeEnvI32(con, 2, srcIdx, dx);
+            * (int* )(partPtr + 4) = dy; //storeEnvI32(con, 2, srcIdx + 1, dy);
+            * (int* )(partPtr + 8) = life; //storeEnvI32(con, 2, srcIdx + 2, life);
+            * (int* )(partPtr + 12) = posx; //storeEnvI32(con, 2, srcIdx + 3, posx);
+            * (int* )(partPtr + 16) = posy; //storeEnvI32(con, 2, srcIdx + 4, posy);
+        }
+
+        partPtr = partPtr + 20;
+    }
+
+    drawTriangleArray(con, loadI32(con, 0, 5), drawCount);
+}
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
index c244d07885872..bc340800e9fcf 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
@@ -111,7 +111,7 @@ public class FountainView extends RSSurfaceView {
 
         mRS.scriptCBegin();
         mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-        mRS.scriptCSetScript("");
+        mRS.scriptCSetScript(getResources(), R.raw.fountain);
         mRS.scriptCSetRoot(true);
         mScript = mRS.scriptCCreate();
 
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
index a62605377c18a..16a94cb3a53f3 100644
--- a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
+++ b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java
@@ -16,7 +16,11 @@
 
 package com.android.fountain;
 
+import java.io.InputStream;
+import java.io.IOException;
+
 import android.os.Bundle;
+import android.content.res.Resources;
 import android.util.Log;
 import android.util.Config;
 import android.view.Menu;
@@ -119,7 +123,7 @@ public class RenderScript {
     native private void nScriptCSetClearStencil(int stencil);
     native private void nScriptCAddType(int type);
     native private void nScriptCSetRoot(boolean isRoot);
-    native private void nScriptCSetScript(String s);
+    native private void nScriptCSetScript(byte[] script, int offset, int length);
     native private int  nScriptCCreate();
 
 
@@ -147,7 +151,7 @@ public class RenderScript {
 
 
     ///////////////////////////////////////////////////////////////////////////////////
-    // 
+    //
 
     RenderScript(Surface sur) {
         mSurface = sur;
@@ -164,14 +168,14 @@ public class RenderScript {
         protected void finalize() throws Throwable
         {
             if (mID != 0) {
-                Log.v(LOG_TAG, 
+                Log.v(LOG_TAG,
                       "Element finalized without having released the RS reference.");
             }
             super.finalize();
         }
     }
 
-    
+
     //////////////////////////////////////////////////////////////////////////////////
     // Element
 
@@ -470,8 +474,8 @@ public class RenderScript {
             break;
         default:
             Log.e(LOG_TAG, "allocationCreateFromBitmap, unknown bitmap format");
-        } 
-        */ 
+        }
+        */
 
         srcFmt = ElementPredefined.RGBA_8888.mID;
 
@@ -609,7 +613,52 @@ public class RenderScript {
     }
 
     public void scriptCSetScript(String s) {
-        nScriptCSetScript(s);
+        try {
+            scriptCSetScript(s.getBytes("UTF-8"));
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void scriptCSetScript(byte[] utf8Bytes) {
+        scriptCSetScript(utf8Bytes, 0, utf8Bytes.length);
+    }
+
+    public void scriptCSetScript(byte[] utf8Bytes, int offset, int length) {
+        nScriptCSetScript(utf8Bytes, offset, length);
+    }
+
+    public void scriptCSetScript(Resources resources, int id) {
+        InputStream is = resources.openRawResource(id);
+        try {
+            try {
+                scriptCSetScript(is);
+            } finally {
+                is.close();
+            }
+        } catch(IOException e) {
+            throw new Resources.NotFoundException();
+        }
+    }
+
+    public void  scriptCSetScript(InputStream is) throws IOException {
+        byte[] buf = new byte[1024];
+        int currentPos = 0;
+        while(true) {
+            int bytesLeft = buf.length - currentPos;
+            if (bytesLeft == 0) {
+                byte[] buf2 = new byte[buf.length * 2];
+                System.arraycopy(buf, 0, buf2, 0, buf.length);
+                buf = buf2;
+                bytesLeft = buf.length - currentPos;
+            }
+            int bytesRead = is.read(buf, currentPos, bytesLeft);
+            if (bytesRead <= 0) {
+                break;
+            }
+            currentPos += bytesRead;
+        }
+        nScriptCSetScript(buf, 0, currentPos);
     }
 
     public Script scriptCCreate() {
diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp
index e1f14b5227ce5..56b4e8814028f 100644
--- a/libs/rs/jni/RenderScript_jni.cpp
+++ b/libs/rs/jni/RenderScript_jni.cpp
@@ -33,256 +33,13 @@
 #include "../RenderScript.h"
 #include "../RenderScriptEnv.h"
 
-#define USE_ACC
-
-#ifdef USE_ACC
 #include "acc/acc.h"
-#endif
 
 //#define LOG_API LOGE
 #define LOG_API(...)
 
 using namespace android;
 
-extern "C" void test_script(void *con, const rsc_FunctionTable *ft, uint32_t launchID);
-
-#ifdef USE_ACC
-static const char* TEST_SCRIPT = ""
-        "// Fountain test script\n"
-        "\n"
-        "main(con, ft, launchID) {\n"
-        "    int count, touch, x, y, rate, maxLife, lifeShift;\n"
-        "    int life;\n"
-        "    int ct, ct2;\n"
-        "    int newPart;\n"
-        "    int drawCount;\n"
-        "    int dx, dy, idx;\n"
-        "    int partPtr;\n"
-        "    int vertPtr;\n"
-        "    int posx,posy;\n"
-        "    int c;\n"
-        "\n"
-        "    count = loadI32(con, 0, 1);\n"
-        "    touch = loadI32(con, 0, 2);\n"
-        "    x = loadI32(con, 0, 3);\n"
-        "    y = 480 - loadI32(con, 0, 4);\n"
-        "\n"
-        "    rate = 4;\n"
-        "    maxLife = (count / rate) - 1;\n"
-        "    lifeShift = 0;\n"
-        "    {\n"
-        "        life = maxLife;\n"
-        "        while (life > 255) {\n"
-        "            life = life >> 1;\n"
-        "            lifeShift ++;\n"
-        "        }\n"
-        "    }\n"
-        "\n"
-        "    contextBindProgramFragment(con, loadI32(con, 0, 7));\n"
-        "    drawRect(con, 0, 256, 0, 512);\n"
-        "    contextBindProgramFragment(con, loadI32(con, 0, 6));\n"
-        "\n"
-        "    if (touch) {\n"
-        "        newPart = loadI32(con, 2, 0);\n"
-        "        for (ct2=0; ct2= count) {\n"
-        "                newPart = 0;\n"
-        "            }\n"
-        "        }\n"
-        "        storeI32(con, 2, 0, newPart);\n"
-        "    }\n"
-        "\n"
-        "    // Emulate intrinsic perf...\n"
-        "    partPtr = loadVp(con, 2, 4);\n"
-        "    vertPtr = loadVp(con, 1, 0);\n"
-        "\n"
-        "    drawCount = 0;\n"
-        "    for (ct=0; ct < count; ct++) {\n"
-        "        //int srcIdx = ct * 5 + 1;\n"
-        "        //int dstIdx = ct * 3 * 3;\n"
-        "\n"
-        "        dx = * (int* )(partPtr + 0); //loadEnvI32(con, 2, srcIdx);\n"
-        "        dy = * (int* )(partPtr + 4); //loadEnvI32(con, 2, srcIdx + 1);\n"
-        "        life = * (int* )(partPtr + 8); //loadEnvI32(con, 2, srcIdx + 2);\n"
-        "        posx = * (int* )(partPtr + 12); //loadEnvI32(con, 2, srcIdx + 3);\n"
-        "        posy = * (int* )(partPtr + 16); //loadEnvI32(con, 2, srcIdx + 4);\n"
-        "\n"
-        "        if (life) {\n"
-        "            if (posy > 0) {\n"
-        "                c = 0xffafcf | ((life >> lifeShift) << 24);\n"
-        "\n"
-        "                * (int* )(vertPtr) = c; //storeEnvU32(con, 1, dstIdx, c);\n"
-        "                * (int* )(vertPtr + 4) = posx; //storeEnvI32(con, 1, dstIdx + 1, posx);\n"
-        "                * (int* )(vertPtr + 8) = posy; //storeEnvI32(con, 1, dstIdx + 2, posy);\n"
-        "\n"
-        "                * (int* )(vertPtr + 12) = c; //storeEnvU32(con, 1, dstIdx + 3, c);\n"
-        "                * (int* )(vertPtr + 16) = posx + 0x10000; //storeEnvI32(con, 1, dstIdx + 4, posx + 0x10000);\n"
-        "                * (int* )(vertPtr + 20) = posy + dy * 4; //storeEnvI32(con, 1, dstIdx + 5, posy);\n"
-        "\n"
-        "                * (int* )(vertPtr + 24) = c; //storeEnvU32(con, 1, dstIdx + 6, c);\n"
-        "                * (int* )(vertPtr + 28) = posx - 0x10000; //storeEnvI32(con, 1, dstIdx + 7, posx + 0x0800);\n"
-        "                * (int* )(vertPtr + 32) = posy + dy * 4; //storeEnvI32(con, 1, dstIdx + 8, posy + 0x10000);\n"
-        "\n"
-        "                vertPtr = vertPtr + 36;\n"
-        "                drawCount ++;\n"
-        "            } else {\n"
-        "                if (dy < 0) {\n"
-        "                    dy = (-dy) >> 1;\n"
-        "                }\n"
-        "            }\n"
-        "\n"
-        "            posx = posx + dx;\n"
-        "            posy = posy + dy;\n"
-        "            dy = dy - 0x400;\n"
-        "            life --;\n"
-        "\n"
-        "            * (int* )(partPtr + 0) = dx; //storeEnvI32(con, 2, srcIdx, dx);\n"
-        "            * (int* )(partPtr + 4) = dy; //storeEnvI32(con, 2, srcIdx + 1, dy);\n"
-        "            * (int* )(partPtr + 8) = life; //storeEnvI32(con, 2, srcIdx + 2, life);\n"
-        "            * (int* )(partPtr + 12) = posx; //storeEnvI32(con, 2, srcIdx + 3, posx);\n"
-        "            * (int* )(partPtr + 16) = posy; //storeEnvI32(con, 2, srcIdx + 4, posy);\n"
-        "        }\n"
-        "\n"
-        "        partPtr = partPtr + 20;\n"
-        "    }\n"
-        "\n"
-        "    drawTriangleArray(con, loadI32(con, 0, 5), drawCount);\n"
-        "}\n";
-
-typedef void (*ScriptEntry)(void *con, const rsc_FunctionTable *ft, uint32_t launchID);
-
-ACCscript* gScript;
-ScriptEntry gScriptEntry;
-
-void test_script(void *con, const rsc_FunctionTable *ft, uint32_t launchID)
-{
-    if (!gScript) {
-        gScript = accCreateScript();
-        const char* scriptSource[] = { TEST_SCRIPT };
-        accScriptSource(gScript, 1, scriptSource, NULL);
-        accCompileScript(gScript);
-        accGetScriptLabel(gScript, "main", (ACCvoid**) &gScriptEntry);
-    }
-    if (gScriptEntry) {
-        gScriptEntry(con, ft, launchID);
-    }
-}
-
-
-#else
-void test_script(void *con, const rsc_FunctionTable *ft, uint32_t launchID)
-{
-    int count = ft->loadEnvI32(con, 0, 1);
-    int touch = ft->loadEnvI32(con, 0, 2);
-    int x = ft->loadEnvI32(con, 0, 3);
-    int y = 480 - ft->loadEnvI32(con, 0, 4);
-
-    int rate = 4;
-    int maxLife = (count / rate) - 1;
-    int lifeShift = 0;
-    {
-        int life = maxLife;
-        while (life > 255) {
-            life >>= 1;
-            lifeShift ++;
-        }
-    }
-
-    ft->contextBindProgramFragment(con, (RsProgramFragment)ft->loadEnvI32(con, 0, 7));
-    ft->drawRect(con, 0, 256, 0, 512);
-    ft->contextBindProgramFragment(con, (RsProgramFragment)ft->loadEnvI32(con, 0, 6));
-
-    if (touch) {
-        int newPart = ft->loadEnvI32(con, 2, 0);
-        for (int ct2=0; ct2rand(con, 0x10000) - 0x8000;
-            int dy = ft->rand(con, 0x10000) - 0x8000;
-
-            int idx = newPart * 5 + 1;
-            ft->storeEnvI32(con, 2, idx, dx);
-            ft->storeEnvI32(con, 2, idx + 1, dy);
-            ft->storeEnvI32(con, 2, idx + 2, maxLife);
-            ft->storeEnvI32(con, 2, idx + 3, x << 16);
-            ft->storeEnvI32(con, 2, idx + 4, y << 16);
-
-            newPart++;
-            if (newPart >= count) {
-                newPart = 0;
-            }
-        }
-        ft->storeEnvI32(con, 2, 0, newPart);
-    }
-
-    // Emulate intrinsic perf...
-    int32_t * partPtr = (int32_t *)ft->loadEnvVp(con, 2, 4);
-    int32_t * vertPtr = (int32_t *)ft->loadEnvVp(con, 1, 0);
-
-    int drawCount = 0;
-    for (int ct=0; ct < count; ct++) {
-        //int srcIdx = ct * 5 + 1;
-        //int dstIdx = ct * 3 * 3;
-
-        int dx = partPtr[0]; //ft->loadEnvI32(con, 2, srcIdx);
-        int dy = partPtr[1]; //ft->loadEnvI32(con, 2, srcIdx + 1);
-        int life = partPtr[2]; //ft->loadEnvI32(con, 2, srcIdx + 2);
-        int posx = partPtr[3]; //ft->loadEnvI32(con, 2, srcIdx + 3);
-        int posy = partPtr[4]; //ft->loadEnvI32(con, 2, srcIdx + 4);
-
-        if (life) {
-            if (posy > 0) {
-                uint32_t c = 0xffafcf | ((life >> lifeShift) << 24);
-
-                ((uint32_t *)vertPtr)[0] = c; //ft->storeEnvU32(con, 1, dstIdx, c);
-                vertPtr[1] = posx; //ft->storeEnvI32(con, 1, dstIdx + 1, posx);
-                vertPtr[2] = posy; //ft->storeEnvI32(con, 1, dstIdx + 2, posy);
-
-                ((uint32_t *)vertPtr)[3] = c; //ft->storeEnvU32(con, 1, dstIdx + 3, c);
-                vertPtr[4] = posx + 0x10000; //ft->storeEnvI32(con, 1, dstIdx + 4, posx + 0x10000);
-                vertPtr[5] = posy + dy * 4; //ft->storeEnvI32(con, 1, dstIdx + 5, posy);
-
-                ((uint32_t *)vertPtr)[6] = c; //ft->storeEnvU32(con, 1, dstIdx + 6, c);
-                vertPtr[7] = posx - 0x10000; //ft->storeEnvI32(con, 1, dstIdx + 7, posx + 0x0800);
-                vertPtr[8] = posy + dy * 4; //ft->storeEnvI32(con, 1, dstIdx + 8, posy + 0x10000);
-
-                vertPtr += 9;
-                drawCount ++;
-            } else {
-                if (dy < 0) {
-                    dy = (-dy) >> 1;
-                }
-            }
-
-            posx += dx;
-            posy += dy;
-            dy -= 0x400;
-            life --;
-
-            partPtr[0] = dx; //ft->storeEnvI32(con, 2, srcIdx, dx);
-            partPtr[1] = dy; //ft->storeEnvI32(con, 2, srcIdx + 1, dy);
-            partPtr[2] = life; //ft->storeEnvI32(con, 2, srcIdx + 2, life);
-            partPtr[3] = posx; //ft->storeEnvI32(con, 2, srcIdx + 3, posx);
-            partPtr[4] = posy; //ft->storeEnvI32(con, 2, srcIdx + 4, posy);
-        }
-
-        partPtr += 5;
-    }
-
-    ft->drawTriangleArray(con, (RsAllocation)ft->loadEnvI32(con, 0, 5), drawCount);
-}
-
-#endif
-
 // ---------------------------------------------------------------------------
 
 static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
@@ -767,12 +524,59 @@ nScriptCSetRoot(JNIEnv *_env, jobject _this, jboolean isRoot)
 }
 
 static void
-nScriptCSetScript(JNIEnv *_env, jobject _this, jboolean isRoot)
+nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef,
+                  jint offset, jint length)
 {
     RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
     LOG_API("!!! nScriptCSetScript, con(%p)", con);
-    //nScriptCSetScript(isRoot);
-    rsScriptCSetScript((void *)test_script);
+    jint _exception = 0;
+    jint remaining;
+    jbyte* script_base = 0;
+    jbyte* script_ptr;
+    void* scriptEntry = 0;
+    if (!scriptRef) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "script == null");
+        goto exit;
+    }
+    if (offset < 0) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "offset < 0");
+        goto exit;
+    }
+    if (length < 0) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "length < 0");
+        goto exit;
+    }
+    remaining = _env->GetArrayLength(scriptRef) - offset;
+    if (remaining < length) {
+        _exception = 1;
+        //_env->ThrowNew(IAEClass, "length > script.length - offset");
+        goto exit;
+    }
+    script_base = (jbyte *)
+        _env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
+    script_ptr = script_base + offset;
+
+    {
+        ACCscript* script = accCreateScript();
+        const char* scriptSource[] = {(const char*) script_ptr};
+        int scriptLength[] = {length} ;
+        accScriptSource(script, 1, scriptSource, scriptLength);
+        accCompileScript(script);
+        accGetScriptLabel(script, "main", (ACCvoid**) &scriptEntry);
+        // TBD: We currently leak the script object. We can't delete it until
+        // we are done with the scriptEntry.
+    }
+    if (scriptEntry) {
+        rsScriptCSetScript((void *)scriptEntry);
+    }
+exit:
+    if (script_base) {
+        _env->ReleasePrimitiveArrayCritical(scriptRef, script_base,
+                _exception ? JNI_ABORT: 0);
+    }
 }
 
 static jint
@@ -998,7 +802,7 @@ static JNINativeMethod methods[] = {
 {"nScriptCSetClearStencil",        "(I)V",                                 (void*)nScriptCSetClearStencil },
 {"nScriptCAddType",                "(I)V",                                 (void*)nScriptCAddType },
 {"nScriptCSetRoot",                "(Z)V",                                 (void*)nScriptCSetRoot },
-{"nScriptCSetScript",              "(Ljava/lang/String;)V",                (void*)nScriptCSetScript },
+{"nScriptCSetScript",              "([BII)V",                              (void*)nScriptCSetScript },
 {"nScriptCCreate",                 "()I",                                  (void*)nScriptCCreate },
 
 {"nProgramFragmentStoreBegin",     "(II)V",                                (void*)nProgramFragmentStoreBegin },

From 088b2918423ef79b92c0b0433f2f59a1fd0f7c1b Mon Sep 17 00:00:00 2001
From: Evan Millar 
Date: Thu, 28 May 2009 15:24:37 -0700
Subject: [PATCH 16/43] Move ContactsContract.java and SocialContract.java into
 android.providers

The ContactsContract and SocialContract lived previously in the
com.android.providers.contacts2 package, and could not be accessed by
other packages from there without symlinks, which was getting messy. If
it turns out there was a good reason for having the contracts in that
package we may have to move these back, but for now this seems like the
obvious configuration.
---
 .../android/provider/ContactsContract.java    | 616 ++++++++++++++++++
 .../java/android/provider/SocialContract.java | 177 +++++
 2 files changed, 793 insertions(+)
 create mode 100644 core/java/android/provider/ContactsContract.java
 create mode 100644 core/java/android/provider/SocialContract.java

diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
new file mode 100644
index 0000000000000..57f9af2786d57
--- /dev/null
+++ b/core/java/android/provider/ContactsContract.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.Im.ProviderNames;
+
+/**
+ * The contract between the contacts provider and applications. Contains definitions
+ * for the supported URIs and columns.
+ *
+ * TODO: move to android.provider
+ */
+public final class ContactsContract {
+    /** The authority for the contacts provider */
+    public static final String AUTHORITY = "com.android.contacts";
+    /** A content:// style uri to the authority for the contacts provider */
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    public interface AggregatesColumns {
+        /**
+         * The display name for the contact.
+         * 

Type: TEXT

+ */ + public static final String DISPLAY_NAME = "display_name"; + + /** + * The number of times a person has been contacted + *

Type: INTEGER

+ */ + public static final String TIMES_CONTACTED = "times_contacted"; + + /** + * The last time a person was contacted. + *

Type: INTEGER

+ */ + public static final String LAST_TIME_CONTACTED = "last_time_contacted"; + + /** + * Is the contact starred? + *

Type: INTEGER (boolean)

+ */ + public static final String STARRED = "starred"; + + /** + * Reference to the row in the data table holding the primary phone number. + *

Type: INTEGER REFERENCES data(_id)

+ */ + public static final String PRIMARY_PHONE_ID = "primary_phone_id"; + + /** + * Reference to the row in the data table holding the primary email address. + *

Type: INTEGER REFERENCES data(_id)

+ */ + public static final String PRIMARY_EMAIL_ID = "primary_email_id"; + + /** + * Reference to the row in the data table holding the photo. + *

Type: INTEGER REFERENCES data(_id)

+ */ + public static final String PHOTO_ID = "photo_id"; + + /** + * Reference to a row containing custom ringtone and send to voicemail information. + *

Type: INTEGER REFERENCES data(_id)

+ */ + public static final String CUSTOM_RINGTONE_ID = "custom_ringtone_id"; + } + + /** + * Constants for the aggregates table, which contains a record per group + * of contact representing the same person. + */ + public static final class Aggregates implements BaseColumns, AggregatesColumns { + /** + * This utility class cannot be instantiated + */ + private Aggregates() {} + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "aggregates"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of + * people. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person_aggregate"; + + /** + * The MIME type of a {@link #CONTENT_URI} subdirectory of a single + * person. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person_aggregate"; + + /** + * A sub-directory of a single contact aggregate that contains all of their + * {@link Data} rows. + */ + public static final class Data implements BaseColumns, DataColumns { + /** + * no public constructor since this is a utility class + */ + private Data() {} + + /** + * The directory twig for this sub-table + */ + public static final String CONTENT_DIRECTORY = "data"; + } + } + + + /** + * Constants for the contacts table, which contains the base contact information. + */ + public static final class Contacts implements BaseColumns { + /** + * This utility class cannot be instantiated + */ + private Contacts() {} + + /** + * A reference to the {@link Aggregates#_ID} that this data belongs to. + */ + public static final String AGGREGATE_ID = "aggregate_id"; + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "contacts"); + + /** + * The content:// style URL for filtering people by email address. The + * filter argument should be passed as an additional path segment after + * this URI. + * + * @hide + */ + public static final Uri CONTENT_FILTER_EMAIL_URI = Uri.withAppendedPath(CONTENT_URI, "filter_email"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of + * people. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person"; + + /** + * The MIME type of a {@link #CONTENT_URI} subdirectory of a single + * person. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person"; + + /** + * A sub-directory of a single contact that contains all of their {@link Data} rows. + * To access this directory append + */ + public static final class Data implements BaseColumns, DataColumns { + /** + * no public constructor since this is a utility class + */ + private Data() {} + + /** + * The directory twig for this sub-table + */ + public static final String CONTENT_DIRECTORY = "data"; + } + } + + private interface DataColumns { + /** + * The package name that defines this type of data. + */ + public static final String PACKAGE = "package"; + + /** + * The mime-type of the item represented by this row. + */ + public static final String MIMETYPE = "mimetype"; + + /** + * A reference to the {@link Contacts#_ID} that this data belongs to. + */ + public static final String CONTACT_ID = "contact_id"; + + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA1 = "data1"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA2 = "data2"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA3 = "data3"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA4 = "data4"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA5 = "data5"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA6 = "data6"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA7 = "data7"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA8 = "data8"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA9 = "data9"; + /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + public static final String DATA10 = "data10"; + } + + /** + * Constants for the data table, which contains data points tied to a contact. + * For example, a phone number or email address. Each row in this table contains a type + * definition and some generic columns. Each data type can define the meaning for each of + * the generic columns. + */ + public static final class Data implements BaseColumns, DataColumns { + /** + * This utility class cannot be instantiated + */ + private Data() {} + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of data. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data"; + } + + /** + * A table that represents the result of looking up a phone number, for example for caller ID. + * The table joins that data row for the phone number with the contact that owns the number. + * To perform a lookup you must append the number you want to find to {@link #CONTENT_URI}. + */ + public static final class PhoneLookup implements BaseColumns, DataColumns, AggregatesColumns { + /** + * This utility class cannot be instantiated + */ + private PhoneLookup() {} + + /** + * The content:// style URI for this table. Append the phone number you want to lookup + * to this URI and query it to perform a lookup. For example: + * + * {@code + * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_URI, phoneNumber); + * } + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "phone_lookup"); + } + + /** + * Container for definitions of common data types stored in the {@link Data} table. + */ + public static final class CommonDataKinds { + /** + * The {@link Data#PACKAGE} value for common data that should be shown + * using a default style. + */ + public static final String PACKAGE_COMMON = "common"; + + /** + * Columns common across the specific types. + */ + private interface BaseCommonColumns { + /** + * The package name that defines this type of data. + */ + public static final String PACKAGE = "package"; + + /** + * The mime-type of the item represented by this row. + */ + public static final String MIMETYPE = "mimetype"; + + /** + * A reference to the {@link Contacts#_ID} that this data belongs to. + */ + public static final String CONTACT_ID = "contact_id"; + } + + /** + * Columns common across the specific types. + */ + private interface CommonColumns { + /** + * The type of data, for example Home or Work. + *

Type: INTEGER

+ */ + public static final String TYPE = "data1"; + + /** + * The user defined label for the the contact method. + *

Type: TEXT

+ */ + public static final String LABEL = "data2"; + + /** + * The data for the contact method. + *

Type: TEXT

+ */ + public static final String DATA = "data3"; + + /** + * Whether this is the primary entry of its kind for the contact it belongs to + *

Type: INTEGER (if set, non-0 means true)

+ */ + public static final String ISPRIMARY = "data4"; + } + + /** + * Parts of the name. + */ + public static final class StructuredName { + private StructuredName() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name"; + + /** + * The contact's honorific prefix, e.g. "Sir" + */ + public static final String PREFIX = "data1"; + + /** + * The given name for the contact. + *

Type: TEXT

+ */ + public static final String GIVEN_NAME = "data2"; + + /** + * The contact's middle name + *

Type: TEXT

+ */ + public static final String MIDDLE_NAME = "data3"; + + /** + * The family name for the contact. + *

Type: TEXT

+ */ + public static final String FAMILY_NAME = "data4"; + + /** + * The contact's honorific suffix, e.g. "Jr" + */ + public static final String SUFFIX = "data5"; + + /** + * The phonetic version of the given name for the contact. + *

Type: TEXT

+ */ + public static final String PHONETIC_GIVEN_NAME = "data6"; + + /** + * The phonetic version of the additional name for the contact. + *

Type: TEXT

+ */ + public static final String PHONETIC_MIDDLE_NAME = "data7"; + + /** + * The phonetic version of the family name for the contact. + *

Type: TEXT

+ */ + public static final String PHONETIC_FAMILY_NAME = "data8"; + + /** + * The name that should be used to display the contact. + *

Type: TEXT

+ */ + public static final String DISPLAY_NAME = "data9"; + } + + /** + * A nickname. + */ + public static final class Nickname { + private Nickname() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/nickname"; + + /** + * The type of data, for example Home or Work. + *

Type: INTEGER

+ */ + public static final String TYPE = "data1"; + + public static final int TYPE_CUSTOM = 1; + public static final int TYPE_DEFAULT = 2; + public static final int TYPE_OTHER_NAME = 3; + public static final int TYPE_MAINDEN_NAME = 4; + public static final int TYPE_SHORT_NAME = 5; + public static final int TYPE_INITIALS = 6; + + /** + * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}. + *

Type: TEXT

+ */ + public static final String LABEL = "data2"; + + /** + * The name itself + */ + public static final String NAME = "data3"; + } + + /** + * Common data definition for telephone numbers. + */ + public static final class Phone implements BaseCommonColumns, CommonColumns { + private Phone() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone"; + + public static final int TYPE_CUSTOM = 0; + public static final int TYPE_HOME = 1; + public static final int TYPE_MOBILE = 2; + public static final int TYPE_WORK = 3; + public static final int TYPE_FAX_WORK = 4; + public static final int TYPE_FAX_HOME = 5; + public static final int TYPE_PAGER = 6; + public static final int TYPE_OTHER = 7; + + /** + * The phone number as the user entered it. + *

Type: TEXT

+ */ + public static final String NUMBER = "data3"; + + } + + /** + * Common data definition for email addresses. + */ + public static final class Email implements BaseCommonColumns, CommonColumns { + private Email() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email"; + + public static final int TYPE_CUSTOM = 0; + public static final int TYPE_HOME = 1; + public static final int TYPE_WORK = 2; + public static final int TYPE_OTHER = 3; + + } + + /** + * Common data definition for postal addresses. + */ + public static final class Postal implements BaseCommonColumns, CommonColumns { + private Postal() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/postal-address"; + + public static final int TYPE_CUSTOM = 0; + public static final int TYPE_HOME = 1; + public static final int TYPE_WORK = 2; + public static final int TYPE_OTHER = 3; + } + + /** + * Common data definition for IM addresses. + */ + public static final class Im implements BaseCommonColumns, CommonColumns { + private Im() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im"; + + public static final int TYPE_CUSTOM = 0; + public static final int TYPE_HOME = 1; + public static final int TYPE_WORK = 2; + public static final int TYPE_OTHER = 3; + + public static final String PROTOCOL = "data5"; + + /** + * The predefined IM protocol types. The protocol can either be non-present, one + * of these types, or a free-form string. These cases are encoded in the PROTOCOL + * column as: + * - null + * - pre: + * - custom:
+ */ + public static final int PROTOCOL_AIM = 0; + public static final int PROTOCOL_MSN = 1; + public static final int PROTOCOL_YAHOO = 2; + public static final int PROTOCOL_SKYPE = 3; + public static final int PROTOCOL_QQ = 4; + public static final int PROTOCOL_GOOGLE_TALK = 5; + public static final int PROTOCOL_ICQ = 6; + public static final int PROTOCOL_JABBER = 7; + } + + /** + * Common data definition for organizations. + */ + public static final class Organization implements BaseCommonColumns { + private Organization() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization"; + + /** + * The type of data, for example Home or Work. + *

Type: INTEGER

+ */ + public static final String TYPE = "data1"; + + public static final int TYPE_CUSTOM = 0; + public static final int TYPE_HOME = 1; + public static final int TYPE_WORK = 2; + public static final int TYPE_OTHER = 3; + + /** + * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}. + *

Type: TEXT

+ */ + public static final String LABEL = "data2"; + + /** + * The company as the user entered it. + *

Type: TEXT

+ */ + public static final String COMPANY = "data3"; + + /** + * The position title at this company as the user entered it. + *

Type: TEXT

+ */ + public static final String TITLE = "data4"; + + /** + * Whether this is the primary organization + *

Type: INTEGER (if set, non-0 means true)

+ */ + public static final String ISPRIMARY = "data5"; + + } + + /** + * Photo of the contact. + */ + public static final class Photo implements BaseCommonColumns { + private Photo() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo"; + + /** + * Thumbnail photo of the contact. This is the raw bytes of an image + * that could be inflated using {@link BitmapFactory}. + *

+ * Type: BLOB + */ + public static final String PHOTO = "data1"; + } + + /** + * Notes about the contact. + */ + public static final class Note implements BaseCommonColumns { + private Note() {} + + /** Mime-type used when storing this in data table. */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/note"; + + /** + * The note text. + *

Type: TEXT

+ */ + public static final String NOTE = "data1"; + } + + public static final class CustomRingtone implements BaseCommonColumns { + private CustomRingtone() {} + + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/custom_ringtone"; + + /** + * Whether to send the number to voicemail. + *

Type: INTEGER (if set, non-0 means true)

+ */ + public static final String SEND_TO_VOICEMAIL = "data1"; + + /** + * The ringtone uri. + *

Type: TEXT

+ */ + public static final String RINGTONE_URI = "data2"; + } + } + +} diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java new file mode 100644 index 0000000000000..f72bbb1840b11 --- /dev/null +++ b/core/java/android/provider/SocialContract.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.provider; + +import android.provider.ContactsContract.Contacts; + +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.provider.BaseColumns; + +/** + * The contract between the social provider and applications. Contains + * definitions for the supported URIs and columns. + *

+ * TODO: move to android.provider package + */ +public class SocialContract { + /** The authority for the social provider */ + public static final String AUTHORITY = "com.android.social"; + + /** A content:// style uri to the authority for the contacts provider */ + public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); + + private interface ActivitiesColumns { + /** + * The package name that owns this social activity. + *

+ * Type: TEXT + */ + public static final String PACKAGE = "package"; + + /** + * The mime-type of this social activity. + *

+ * Type: TEXT + */ + public static final String MIMETYPE = "mimetype"; + + /** + * Internal raw identifier for this social activity. This field is + * analogous to the atom:id element defined in RFC 4287. + *

+ * Type: TEXT + */ + public static final String RAW_ID = "raw_id"; + + /** + * Reference to another {@link Activities#RAW_ID} that this social activity + * is replying to. This field is analogous to the + * thr:in-reply-to element defined in RFC 4685. + *

+ * Type: TEXT + */ + public static final String IN_REPLY_TO = "in_reply_to"; + + /** + * Reference to the {@link Contacts#_ID} that authored this social + * activity. This field is analogous to the atom:author + * element defined in RFC 4287. + *

+ * Type: INTEGER + */ + public static final String AUTHOR_CONTACT_ID = "author_contact_id"; + + /** + * Optional reference to the {@link Contacts#_ID} this social activity + * is targeted towards. If more than one direct target, this field may + * be left undefined. This field is analogous to the + * activity:target element defined in the Atom Activity + * Extensions Internet-Draft. + *

+ * Type: INTEGER + */ + public static final String TARGET_CONTACT_ID = "target_contact_id"; + + /** + * Timestamp when this social activity was published, in a + * {@link System#currentTimeMillis()} time base. This field is analogous + * to the atom:published element defined in RFC 4287. + *

+ * Type: INTEGER + */ + public static final String PUBLISHED = "published"; + + /** + * Timestamp when the original social activity in a thread was + * published. For activities that have an in-reply-to field specified, the + * content provider will automatically populate this field with the + * timestamp of the original activity. + *

+ * This field is useful for sorting order of activities that keeps together all + * messages in each thread. + *

+ * Type: INTEGER + */ + public static final String THREAD_PUBLISHED = "thread_published"; + + /** + * Title of this social activity. This field is analogous to the + * atom:title element defined in RFC 4287. + *

+ * Type: TEXT + */ + public static final String TITLE = "title"; + + /** + * Summary of this social activity. This field is analogous to the + * atom:summary element defined in RFC 4287. + *

+ * Type: TEXT + */ + public static final String SUMMARY = "summary"; + + /** + * A URI associated this social activity. This field is analogous to the + * atom:link rel="alternate" element defined in RFC 4287. + *

+ * Type: TEXT + */ + public static final String LINK = "link"; + + /** + * Optional thumbnail specific to this social activity. This is the raw + * bytes of an image that could be inflated using {@link BitmapFactory}. + *

+ * Type: BLOB + */ + public static final String THUMBNAIL = "thumbnail"; + } + + public static final class Activities implements BaseColumns, ActivitiesColumns { + /** + * This utility class cannot be instantiated + */ + private Activities() { + } + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "activities"); + + /** + * The content:// style URI for this table filtered to the set of + * social activities authored by a specific {@link Contact#_ID}. + */ + public static final Uri CONTENT_AUTHORED_BY_URI = + Uri.withAppendedPath(CONTENT_URI, "authored_by"); + + /** + * The MIME type of {@link #CONTENT_URI} providing a directory of social + * activities. + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/activity"; + + /** + * The MIME type of a {@link #CONTENT_URI} subdirectory of a single + * social activity. + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/activity"; + } + +} From 63975dde40e4ee8fd08225741b31d54eff115104 Mon Sep 17 00:00:00 2001 From: Jack Palevich Date: Thu, 28 May 2009 15:34:15 -0700 Subject: [PATCH 17/43] Remove versions of scriptCSetScript method that take byte arrays. This makes the API simpler, and therefore probably easier to use. --- .../src/com/android/fountain/RenderScript.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java index 16a94cb3a53f3..739f9ae06533e 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java @@ -614,20 +614,13 @@ public class RenderScript { public void scriptCSetScript(String s) { try { - scriptCSetScript(s.getBytes("UTF-8")); + byte[] bytes = s.getBytes("UTF-8"); + nScriptCSetScript(bytes, 0, bytes.length); } catch (java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } } - public void scriptCSetScript(byte[] utf8Bytes) { - scriptCSetScript(utf8Bytes, 0, utf8Bytes.length); - } - - public void scriptCSetScript(byte[] utf8Bytes, int offset, int length) { - nScriptCSetScript(utf8Bytes, offset, length); - } - public void scriptCSetScript(Resources resources, int id) { InputStream is = resources.openRawResource(id); try { From ec5a20bf2b9f563f9f6a4bde4d8cbbe13598fd25 Mon Sep 17 00:00:00 2001 From: Jack Palevich Date: Thu, 28 May 2009 15:53:04 -0700 Subject: [PATCH 18/43] Delete the acc script when deleting the Renderscript rsScriptC object. Previously we had been leaking the ACCscript object. --- libs/rs/Android.mk | 2 +- libs/rs/jni/RenderScript_jni.cpp | 11 +++++++---- libs/rs/rs.spec | 3 ++- libs/rs/rsScriptC.cpp | 15 ++++++++++++++- libs/rs/rsScriptC.h | 9 ++++++--- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 7ae5709792f12..979c4418590ed 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -94,7 +94,7 @@ LOCAL_SRC_FILES:= \ rsType.cpp \ rsTriangleMesh.cpp -LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui +LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui libacc LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libRS diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp index 56b4e8814028f..50849afebbfa1 100644 --- a/libs/rs/jni/RenderScript_jni.cpp +++ b/libs/rs/jni/RenderScript_jni.cpp @@ -533,6 +533,7 @@ nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef, jint remaining; jbyte* script_base = 0; jbyte* script_ptr; + ACCscript* script = 0; void* scriptEntry = 0; if (!scriptRef) { _exception = 1; @@ -560,19 +561,21 @@ nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef, script_ptr = script_base + offset; { - ACCscript* script = accCreateScript(); + script = accCreateScript(); const char* scriptSource[] = {(const char*) script_ptr}; int scriptLength[] = {length} ; accScriptSource(script, 1, scriptSource, scriptLength); accCompileScript(script); accGetScriptLabel(script, "main", (ACCvoid**) &scriptEntry); - // TBD: We currently leak the script object. We can't delete it until - // we are done with the scriptEntry. } if (scriptEntry) { - rsScriptCSetScript((void *)scriptEntry); + rsScriptCSetScript((void*) script, (void *)scriptEntry); + script = 0; } exit: + if (script) { + accDeleteScript(script); + } if (script_base) { _env->ReleasePrimitiveArrayCritical(scriptRef, script_base, _exception ? JNI_ABORT: 0); diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 65a4a8261ef07..98c70080761bf 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -288,7 +288,8 @@ ScriptCSetOrtho { } ScriptCSetScript { - param void * ptr + param void * accScript + param void * codePtr } ScriptCCreate { diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 60339ecdc2d89..2c7d88415f5c5 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -18,17 +18,23 @@ #include "rsScriptC.h" #include "rsMatrix.h" +#include "acc/acc.h" + using namespace android; using namespace android::renderscript; ScriptC::ScriptC() { + mAccScript = NULL; mScript = NULL; } ScriptC::~ScriptC() { + if (mAccScript) { + accDeleteScript(mAccScript); + } } extern "C" void matrixLoadIdentity(void *con, rsc_Matrix *mat) @@ -394,6 +400,9 @@ ScriptCState::ScriptCState() ScriptCState::~ScriptCState() { + if (mAccScript) { + accDeleteScript(mAccScript); + } } void ScriptCState::clear() @@ -405,6 +414,7 @@ void ScriptCState::clear() mClearColor[3] = 1; mClearDepth = 1; mClearStencil = 0; + mAccScript = NULL; mScript = NULL; mIsRoot = false; mIsOrtho = true; @@ -446,9 +456,10 @@ void rsi_ScriptCAddType(Context * rsc, RsType vt) ss->mConstantBufferTypes.add(static_cast(vt)); } -void rsi_ScriptCSetScript(Context * rsc, void *vp) +void rsi_ScriptCSetScript(Context * rsc, void* accScript, void *vp) { ScriptCState *ss = &rsc->mScriptC; + ss->mAccScript = reinterpret_cast(accScript); ss->mScript = reinterpret_cast(vp); } @@ -469,6 +480,8 @@ RsScript rsi_ScriptCCreate(Context * rsc) ScriptCState *ss = &rsc->mScriptC; ScriptC *s = new ScriptC(); + s->mAccScript = ss->mAccScript; + ss->mAccScript = NULL; s->mScript = ss->mScript; s->mClearColor[0] = ss->mClearColor[0]; s->mClearColor[1] = ss->mClearColor[1]; diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index f35a130767cac..c58dcf9d4b1ba 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -21,11 +21,13 @@ #include "RenderScriptEnv.h" +struct ACCscript; + // --------------------------------------------------------------------------- namespace android { namespace renderscript { - + class ScriptC : public Script { @@ -38,6 +40,7 @@ public: virtual void run(Context *, uint32_t launchID); + ACCscript* mAccScript; rsc_RunScript mScript; @@ -48,13 +51,13 @@ public: }; -class ScriptCState +class ScriptCState { public: ScriptCState(); ~ScriptCState(); - + ACCscript* mAccScript; rsc_RunScript mScript; float mClearColor[4]; float mClearDepth; From 02fb2cb531035779a25dbf9595e0628ea40585b0 Mon Sep 17 00:00:00 2001 From: Jason Sams Date: Thu, 28 May 2009 15:37:57 -0700 Subject: [PATCH 19/43] Add sampler support --- .../com/android/fountain/FountainView.java | 8 +++ .../com/android/fountain/RenderScript.java | 69 +++++++++++++++++-- libs/rs/jni/RenderScript_jni.cpp | 48 ++++++++++--- libs/rs/rs.spec | 8 +-- libs/rs/rsProgramFragment.cpp | 8 +-- libs/rs/rsSampler.cpp | 24 ++++++- 6 files changed, 140 insertions(+), 25 deletions(-) diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java index bc340800e9fcf..338152598bd75 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java @@ -52,6 +52,7 @@ public class FountainView extends RSSurfaceView { private RenderScript.ProgramFragment mPF; private RenderScript.ProgramFragment mPF2; private RenderScript.Allocation mTexture; + private RenderScript.Sampler mSampler; private Bitmap mBackground; @@ -83,6 +84,12 @@ public class FountainView extends RSSurfaceView { mPFS = mRS.programFragmentStoreCreate(); mRS.contextBindProgramFragmentStore(mPFS); + mRS.samplerBegin(); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.LINEAR); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.LINEAR); + mSampler = mRS.samplerCreate(); + + mRS.programFragmentBegin(null, null); mPF = mRS.programFragmentCreate(); //mRS.contextBindProgramFragment(mPF); @@ -92,6 +99,7 @@ public class FountainView extends RSSurfaceView { mPF2 = mRS.programFragmentCreate(); mRS.contextBindProgramFragment(mPF2); mPF2.bindTexture(mTexture, 0); + mPF2.bindSampler(mSampler, 0); mParams[0] = 0; mParams[1] = partCount; diff --git a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java index 739f9ae06533e..cf16cec7c5a5c 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/RenderScript.java @@ -38,7 +38,7 @@ public class RenderScript { - /* + /* * We use a class initializer to allow the native code to cache some * field offsets. */ @@ -49,9 +49,7 @@ public class RenderScript { sInitialized = false; try { System.loadLibrary("RS_jni"); - Log.e(LOG_TAG, "*** Renderscript INIT"); _nInit(); - Log.e(LOG_TAG, "*** Renderscript INIT 3"); sInitialized = true; } catch (UnsatisfiedLinkError e) { Log.d(LOG_TAG, "RenderScript JNI library not found!"); @@ -126,6 +124,10 @@ public class RenderScript { native private void nScriptCSetScript(byte[] script, int offset, int length); native private int nScriptCCreate(); + native private void nSamplerDestroy(int sampler); + native private void nSamplerBegin(); + native private void nSamplerSet(int param, int value); + native private int nSamplerCreate(); native private void nProgramFragmentStoreBegin(int in, int out); native private void nProgramFragmentStoreDepthFunc(int func); @@ -307,6 +309,34 @@ public class RenderScript { } } + public enum SamplerParam { + FILTER_MIN (0), + FILTER_MAG (1), + WRAP_MODE_S (2), + WRAP_MODE_T (3), + WRAP_MODE_R (4); + + int mID; + SamplerParam(int id) { + mID = id; + } + } + + public enum SamplerValue { + NEAREST (0), + LINEAR (1), + LINEAR_MIP_LINEAR (2), + WRAP (3), + CLAMP (4); + + int mID; + SamplerValue(int id) { + mID = id; + } + } + + + public class Element extends BaseObj { Element(int id) { mID = id; @@ -727,9 +757,9 @@ public class RenderScript { nProgramFragmentBindTexture(mID, slot, va.mID); } - //public void bindSampler(Sampler vs, int slot) { - //nProgramFragmentBindSampler(mID, slot, vs.mID); - //} + public void bindSampler(Sampler vs, int slot) { + nProgramFragmentBindSampler(mID, slot, vs.mID); + } } public void programFragmentBegin(Element in, Element out) { @@ -761,6 +791,33 @@ public class RenderScript { return new ProgramFragment(id); } + ////////////////////////////////////////////////////////////////////////////////// + // Sampler + + public class Sampler extends BaseObj { + Sampler(int id) { + mID = id; + } + + public void destroy() { + nSamplerDestroy(mID); + mID = 0; + } + } + + public void samplerBegin() { + nSamplerBegin(); + } + + public void samplerSet(SamplerParam p, SamplerValue v) { + nSamplerSet(p.mID, v.mID); + } + + public Sampler samplerCreate() { + int id = nSamplerCreate(); + return new Sampler(id); + } + /////////////////////////////////////////////////////////////////////////////////// // Root state diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp index 50849afebbfa1..4af58a67d2b6e 100644 --- a/libs/rs/jni/RenderScript_jni.cpp +++ b/libs/rs/jni/RenderScript_jni.cpp @@ -717,14 +717,6 @@ nContextBindRootScript(JNIEnv *_env, jobject _this, jint script) rsContextBindRootScript((RsScript)script); } -static void -nContextBindSampler(JNIEnv *_env, jobject _this, jint sampler, jint slot) -{ - RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nContextBindSampler, con(%p), sampler(%p), slot(%i)", con, (RsSampler)sampler, slot); - rsContextBindSampler(slot, (RsSampler)sampler); -} - static void nContextBindProgramFragmentStore(JNIEnv *_env, jobject _this, jint pfs) { @@ -741,6 +733,40 @@ nContextBindProgramFragment(JNIEnv *_env, jobject _this, jint pf) rsContextBindProgramFragment((RsProgramFragment)pf); } +// --------------------------------------------------------------------------- + +static void +nSamplerDestroy(JNIEnv *_env, jobject _this, jint s) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nSamplerDestroy, con(%p), sampler(%p)", con, (RsSampler)s); + rsSamplerDestroy((RsSampler)s); +} + +static void +nSamplerBegin(JNIEnv *_env, jobject _this) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nSamplerBegin, con(%p)", con); + rsSamplerBegin(); +} + +static void +nSamplerSet(JNIEnv *_env, jobject _this, jint p, jint v) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nSamplerSet, con(%p), param(%i), value(%i)", con, p, v); + rsSamplerSet((RsSamplerParam)p, (RsSamplerValue)v); +} + +static jint +nSamplerCreate(JNIEnv *_env, jobject _this) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + LOG_API("nSamplerCreate, con(%p), script(%p)", con, (RsScript)script); + return (jint)rsSamplerCreate(); +} + // --------------------------------------------------------------------------- @@ -825,10 +851,14 @@ static JNINativeMethod methods[] = { {"nProgramFragmentCreate", "()I", (void*)nProgramFragmentCreate }, {"nContextBindRootScript", "(I)V", (void*)nContextBindRootScript }, -//{"nContextBindSampler", "(II)V", (void*)nContextBindSampler }, {"nContextBindProgramFragmentStore","(I)V", (void*)nContextBindProgramFragmentStore }, {"nContextBindProgramFragment", "(I)V", (void*)nContextBindProgramFragment }, +{"nSamplerDestroy", "(I)V", (void*)nSamplerDestroy }, +{"nSamplerBegin", "()V", (void*)nSamplerBegin }, +{"nSamplerSet", "(II)V", (void*)nSamplerSet }, +{"nSamplerCreate", "()I", (void*)nSamplerCreate }, + }; static int registerFuncs(JNIEnv *_env) diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 98c70080761bf..090be3219c22e 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -1,10 +1,5 @@ -ContextBindSampler { - param uint32_t slot - param RsSampler sampler - } - ContextBindRootScript { param RsScript sampler } @@ -212,6 +207,9 @@ SamplerCreate { ret RsSampler } +SamplerDestroy { + param RsSampler s + } TriangleMeshBegin { param RsElement vertex diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 1a6e2c7fb51ac..3d316ea1cbda9 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -62,14 +62,14 @@ void ProgramFragment::setupGL() break; } -// if (mSamplers[ct].get()) { - //mSamplers[ct]->setupGL(); -// } else { + if (mSamplers[ct].get()) { + mSamplers[ct]->setupGL(); + } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - //} + } } glActiveTexture(GL_TEXTURE0); } diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp index 3c008c9de8cc4..ca407db40381d 100644 --- a/libs/rs/rsSampler.cpp +++ b/libs/rs/rsSampler.cpp @@ -53,7 +53,21 @@ Sampler::~Sampler() void Sampler::setupGL() { + GLenum translate[] = { + GL_NEAREST, //RS_SAMPLER_NEAREST, + GL_LINEAR, //RS_SAMPLER_LINEAR, + GL_LINEAR_MIP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR, + GL_WRAP, //RS_SAMPLER_WRAP, + GL_CLAMP_TO_EDGS, //RS_SAMPLER_CLAMP + + } + + //LOGE("setup gl"); + switch(mMagFilter) { + case RS_SAMPLER_ + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -76,7 +90,7 @@ void Sampler::unbindFromContext(SamplerState *ss) void SamplerState::setupGL() { - for (uint32_t ct=0; ct < 1/*RS_MAX_SAMPLER_SLOT*/; ct++) { + for (uint32_t ct=0; ct < RS_MAX_SAMPLER_SLOT; ct++) { Sampler *s = mSamplers[ct].get(); if (s) { s->setupGL(); @@ -140,4 +154,12 @@ RsSampler rsi_SamplerCreate(Context *rsc) return s; } +void rsi_SamplerDestroy(Context *rsc, RsSampler vs) +{ + Sampler * s = static_cast(vs); + s->decRef(); + +} + + }} From 243e3fbcf710115c5b4b61e1112e61cc2268478a Mon Sep 17 00:00:00 2001 From: Jason Sams Date: Thu, 28 May 2009 16:16:24 -0700 Subject: [PATCH 20/43] Implement sampler GL setup code. --- libs/rs/rsSampler.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp index ca407db40381d..d89346e16cc98 100644 --- a/libs/rs/rsSampler.cpp +++ b/libs/rs/rsSampler.cpp @@ -53,25 +53,20 @@ Sampler::~Sampler() void Sampler::setupGL() { - GLenum translate[] = { + GLenum trans[] = { GL_NEAREST, //RS_SAMPLER_NEAREST, GL_LINEAR, //RS_SAMPLER_LINEAR, - GL_LINEAR_MIP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR, - GL_WRAP, //RS_SAMPLER_WRAP, - GL_CLAMP_TO_EDGS, //RS_SAMPLER_CLAMP + GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR, + GL_REPEAT, //RS_SAMPLER_WRAP, + GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP - } + }; - //LOGE("setup gl"); - switch(mMagFilter) { - case RS_SAMPLER_ - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]); } From 569277732b4180c07c9f917ff8c3fc3111b10338 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Thu, 28 May 2009 17:23:39 -0700 Subject: [PATCH 21/43] Fixing javadoc references. --- .../android/provider/ContactsContract.java | 30 +++++++++---------- .../java/android/provider/SocialContract.java | 12 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 57f9af2786d57..0485a1083388c 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -18,8 +18,6 @@ package android.provider; import android.graphics.BitmapFactory; import android.net.Uri; -import android.provider.BaseColumns; -import android.provider.Im.ProviderNames; /** * The contract between the contacts provider and applications. Contains definitions @@ -63,13 +61,13 @@ public final class ContactsContract { *

Type: INTEGER REFERENCES data(_id)

*/ public static final String PRIMARY_PHONE_ID = "primary_phone_id"; - + /** * Reference to the row in the data table holding the primary email address. *

Type: INTEGER REFERENCES data(_id)

*/ public static final String PRIMARY_EMAIL_ID = "primary_email_id"; - + /** * Reference to the row in the data table holding the photo. *

Type: INTEGER REFERENCES data(_id)

@@ -151,7 +149,7 @@ public final class ContactsContract { * The content:// style URL for filtering people by email address. The * filter argument should be passed as an additional path segment after * this URI. - * + * * @hide */ public static final Uri CONTENT_FILTER_EMAIL_URI = Uri.withAppendedPath(CONTENT_URI, "filter_email"); @@ -197,7 +195,8 @@ public final class ContactsContract { public static final String MIMETYPE = "mimetype"; /** - * A reference to the {@link Contacts#_ID} that this data belongs to. + * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} + * that this data belongs to. */ public static final String CONTACT_ID = "contact_id"; @@ -293,7 +292,8 @@ public final class ContactsContract { public static final String MIMETYPE = "mimetype"; /** - * A reference to the {@link Contacts#_ID} that this data belongs to. + * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} that this + * data belongs to. */ public static final String CONTACT_ID = "contact_id"; } @@ -446,7 +446,7 @@ public final class ContactsContract { *

Type: TEXT

*/ public static final String NUMBER = "data3"; - + } /** @@ -493,9 +493,9 @@ public final class ContactsContract { public static final int TYPE_HOME = 1; public static final int TYPE_WORK = 2; public static final int TYPE_OTHER = 3; - + public static final String PROTOCOL = "data5"; - + /** * The predefined IM protocol types. The protocol can either be non-present, one * of these types, or a free-form string. These cases are encoded in the PROTOCOL @@ -557,7 +557,7 @@ public final class ContactsContract { *

Type: INTEGER (if set, non-0 means true)

*/ public static final String ISPRIMARY = "data5"; - + } /** @@ -593,18 +593,18 @@ public final class ContactsContract { */ public static final String NOTE = "data1"; } - + public static final class CustomRingtone implements BaseCommonColumns { private CustomRingtone() {} - + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/custom_ringtone"; - + /** * Whether to send the number to voicemail. *

Type: INTEGER (if set, non-0 means true)

*/ public static final String SEND_TO_VOICEMAIL = "data1"; - + /** * The ringtone uri. *

Type: TEXT

diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java index f72bbb1840b11..8254851d2d361 100644 --- a/core/java/android/provider/SocialContract.java +++ b/core/java/android/provider/SocialContract.java @@ -68,8 +68,8 @@ public class SocialContract { public static final String IN_REPLY_TO = "in_reply_to"; /** - * Reference to the {@link Contacts#_ID} that authored this social - * activity. This field is analogous to the atom:author + * Reference to the {@link android.provider.ContactsContract.Contacts#_ID} that authored + * this social activity. This field is analogous to the atom:author * element defined in RFC 4287. *

* Type: INTEGER @@ -77,8 +77,8 @@ public class SocialContract { public static final String AUTHOR_CONTACT_ID = "author_contact_id"; /** - * Optional reference to the {@link Contacts#_ID} this social activity - * is targeted towards. If more than one direct target, this field may + * Optional reference to the {@link android.provider.ContactsContract.Contacts#_ID} this + * social activity is targeted towards. If more than one direct target, this field may * be left undefined. This field is analogous to the * activity:target element defined in the Atom Activity * Extensions Internet-Draft. @@ -155,8 +155,8 @@ public class SocialContract { public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "activities"); /** - * The content:// style URI for this table filtered to the set of - * social activities authored by a specific {@link Contact#_ID}. + * The content:// style URI for this table filtered to the set of social activities + * authored by a specific {@link android.provider.ContactsContract.Contacts#_ID}. */ public static final Uri CONTENT_AUTHORED_BY_URI = Uri.withAppendedPath(CONTENT_URI, "authored_by"); From dda5391d5079537e275c9f4ed2637a1484d0e4e8 Mon Sep 17 00:00:00 2001 From: Wink Saville Date: Thu, 28 May 2009 17:32:34 -0700 Subject: [PATCH 22/43] Motorola additions for CDMA support without CdmaSuppConnTracker There are corresponding changes to hardware/ril and packages/apps/Phone that are required to go with these changes. --- core/java/android/provider/CallLog.java | 18 + .../text/method/DialerKeyListener.java | 2 +- media/java/android/media/ToneGenerator.java | 319 +++++++++++-- .../internal/telephony/BaseCommands.java | 59 ++- .../internal/telephony/CallTracker.java | 1 + .../internal/telephony/CallerInfo.java | 9 +- .../telephony/CdmaInformationRecord.java | 201 --------- .../internal/telephony/CommandsInterface.java | 75 ++-- .../internal/telephony/Connection.java | 33 +- .../internal/telephony/ITelephony.aidl | 5 + .../com/android/internal/telephony/Phone.java | 196 +++++--- .../android/internal/telephony/PhoneBase.java | 114 +++-- .../internal/telephony/PhoneProxy.java | 94 ++-- .../com/android/internal/telephony/RIL.java | 272 +++++++----- .../internal/telephony/TelephonyIntents.java | 34 +- .../telephony/TelephonyProperties.java | 3 + .../internal/telephony/cdma/CDMAPhone.java | 418 ++++++++++++------ .../telephony/cdma/CdmaCallTracker.java | 32 +- .../telephony/cdma/CdmaCallWaiting.java | 33 -- .../cdma/CdmaCallWaitingNotification.java | 48 ++ .../telephony/cdma/CdmaConnection.java | 269 +++++++++-- .../cdma/CdmaInformationRecords.java | 75 ++++ .../cdma/CdmaServiceStateTracker.java | 299 ++++++++++++- .../internal/telephony/cdma/FeatureCode.java | 14 +- .../internal/telephony/cdma/RuimRecords.java | 46 +- .../telephony/cdma/SignalToneUtil.java | 272 ++++++++++++ .../internal/telephony/gsm/GSMPhone.java | 5 - .../telephony/test/SimulatedCommands.java | 6 +- 28 files changed, 2169 insertions(+), 783 deletions(-) delete mode 100644 telephony/java/com/android/internal/telephony/CdmaInformationRecord.java delete mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaCallWaiting.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java create mode 100644 telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index abd6934a86b39..7d03801d7bd10 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -151,6 +151,9 @@ public class CallLog { int presentation, int callType, long start, int duration) { final ContentResolver resolver = context.getContentResolver(); + // TODO(Moto): Which is correct: original code, this only changes the + // number if the number is empty and never changes the caller info name. + if (false) { if (TextUtils.isEmpty(number)) { if (presentation == Connection.PRESENTATION_RESTRICTED) { number = CallerInfo.PRIVATE_NUMBER; @@ -160,7 +163,22 @@ public class CallLog { number = CallerInfo.UNKNOWN_NUMBER; } } + } else { + // NEWCODE: From Motorola + //If this is a private number then set the number to Private, otherwise check + //if the number field is empty and set the number to Unavailable + if (presentation == Connection.PRESENTATION_RESTRICTED) { + number = CallerInfo.PRIVATE_NUMBER; + ci.name = ""; + } else if (presentation == Connection.PRESENTATION_PAYPHONE) { + number = CallerInfo.PAYPHONE_NUMBER; + ci.name = ""; + } else if (TextUtils.isEmpty(number) || presentation == Connection.PRESENTATION_UNKNOWN) { + number = CallerInfo.UNKNOWN_NUMBER; + ci.name = ""; + } + } ContentValues values = new ContentValues(5); values.put(NUMBER, number); diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java index b121e608b5b54..584e83f53e53d 100644 --- a/core/java/android/text/method/DialerKeyListener.java +++ b/core/java/android/text/method/DialerKeyListener.java @@ -106,7 +106,7 @@ public class DialerKeyListener extends NumberKeyListener */ public static final char[] CHARACTERS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*', - '+', '-', '(', ')', ',', '/', 'N', '.', ' ' + '+', '-', '(', ')', ',', '/', 'N', '.', ' ', ';' }; private static DialerKeyListener sInstance; diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java index 4b53756db118a..54ca6c40c5637 100644 --- a/media/java/android/media/ToneGenerator.java +++ b/media/java/android/media/ToneGenerator.java @@ -19,11 +19,11 @@ package android.media; /** - * This class provides methods to play DTMF tones (ITU-T Recommendation Q.23), - * call supervisory tones (3GPP TS 22.001, CEPT) and proprietary tones (3GPP TS 31.111). + * This class provides methods to play DTMF tones (ITU-T Recommendation Q.23), + * call supervisory tones (3GPP TS 22.001, CEPT) and proprietary tones (3GPP TS 31.111). * Depending on call state and routing options, tones are mixed to the downlink audio - * or output to the speaker phone or headset. - * This API is not for generating tones over the uplink audio path. + * or output to the speaker phone or headset. + * This API is not for generating tones over the uplink audio path. */ public class ToneGenerator { @@ -33,99 +33,99 @@ public class ToneGenerator * List of all available tones: These constants must be kept consistant with * the enum in ToneGenerator C++ class. */ - /** + /** * DTMF tone for key 0: 1336Hz, 941Hz, continuous

- * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_0 = 0; /** * DTMF tone for key 1: 1209Hz, 697Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_1 = 1; /** * DTMF tone for key 2: 1336Hz, 697Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_2 = 2; /** * DTMF tone for key 3: 1477Hz, 697Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_3 = 3; /** * DTMF tone for key 4: 1209Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_4 = 4; /** * DTMF tone for key 5: 1336Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_5 = 5; /** * DTMF tone for key 6: 1477Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_6 = 6; /** * DTMF tone for key 7: 1209Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_7 = 7; /** * DTMF tone for key 8: 1336Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_8 = 8; /** * DTMF tone for key 9: 1477Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_9 = 9; /** * DTMF tone for key *: 1209Hz, 941Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_S = 10; /** * DTMF tone for key #: 1477Hz, 941Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_P = 11; /** * DTMF tone for key A: 1633Hz, 697Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_A = 12; /** * DTMF tone for key B: 1633Hz, 770Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_B = 13; /** * DTMF tone for key C: 1633Hz, 852Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_C = 14; /** * DTMF tone for key D: 1633Hz, 941Hz, continuous - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_DTMF_D = 15; @@ -151,7 +151,7 @@ public class ToneGenerator * Call supervisory tone, Congestion: * CEPT, JAPAN: 425Hz, 200ms ON, 200ms OFF... * ANSI (IS-95): 480Hz+620Hz, 250ms ON, 250ms OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_CONGESTION = 18; @@ -159,27 +159,28 @@ public class ToneGenerator * Call supervisory tone, Radio path acknowlegment : * CEPT, ANSI: 425Hz, 200ms ON * JAPAN: 400Hz, 1s ON, 2s OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_RADIO_ACK = 19; /** * Call supervisory tone, Radio path not available: 425Hz, 200ms ON, 200 OFF 3 bursts - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_RADIO_NOTAVAIL = 20; /** * Call supervisory tone, Error/Special info: 950Hz+1400Hz+1800Hz, 330ms ON, 1s OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_ERROR = 21; /** * Call supervisory tone, Call Waiting: * CEPT, JAPAN: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... - * ANSI (IS-95): 440 Hz, 300 ms ON, 9.7 s OFF, (100 ms ON, 100 ms OFF, 100 ms ON, 9.7s OFF ...) - * + * ANSI (IS-95): 440 Hz, 300 ms ON, 9.7 s OFF, + * (100 ms ON, 100 ms OFF, 100 ms ON, 9.7s OFF ...) + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_CALL_WAITING = 22; @@ -187,42 +188,43 @@ public class ToneGenerator * Call supervisory tone, Ring Tone: * CEPT, JAPAN: 425Hz, 1s ON, 4s OFF... * ANSI (IS-95): 440Hz + 480Hz, 2s ON, 4s OFF... - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_RINGTONE = 23; /** * Proprietary tone, general beep: 400Hz+1200Hz, 35ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_BEEP = 24; /** * Proprietary tone, positive acknowlegement: 1200Hz, 100ms ON, 100ms OFF 2 bursts - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_ACK = 25; /** * Proprietary tone, negative acknowlegement: 300Hz+400Hz+500Hz, 400ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_NACK = 26; /** * Proprietary tone, prompt tone: 400Hz+1200Hz, 200ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_PROMPT = 27; /** * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON - * + * * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_BEEP2 = 28; /** - * Call supervisory tone (IS-95), intercept tone: alternating 440 Hz and 620 Hz tones, each on for 250 ms + * Call supervisory tone (IS-95), intercept tone: alternating 440 Hz and 620 Hz tones, + * each on for 250 ms * * @see #ToneGenerator(int, int) */ @@ -240,7 +242,8 @@ public class ToneGenerator */ public static final int TONE_SUP_CONGESTION_ABBREV = 31; /** - * Call supervisory tone (IS-95), confirm tone: a 350 Hz tone added to a 440 Hz tone repeated 3 times in a 100 ms on, 100 ms off cycle + * Call supervisory tone (IS-95), confirm tone: a 350 Hz tone added to a 440 Hz tone + * repeated 3 times in a 100 ms on, 100 ms off cycle * * @see #ToneGenerator(int, int) */ @@ -253,6 +256,241 @@ public class ToneGenerator public static final int TONE_SUP_PIP = 33; + /** + * CDMA SPECIFIC TONES START + */ + + /** TODO(Moto): Change "Proprietary" below with an appropriate specification reference */ + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_DIAL_TONE_LITE = 34; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_NETWORK_USA_RINGBACK = 35; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_REORDER = 36; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_ABBR_REORDER = 37; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_NETWORK_BUSY = 38; + + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_ANSWER = 39; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_NETWORK_CALLWAITING = 40; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_PIP = 41; + + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL = 42; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP = 43; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_SP_PRI = 44; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT3 = 45; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_RING_RING = 46; + + /** + * Proprietary tone, general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT5 = 47; + + /** + * general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT6 = 48; + + /** + * general double beep: twice 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms ON + * + * @see #ToneGenerator(int, int) + * + * @hide + */ + public static final int TONE_CDMA_CALL_SIGNAL_ISDN_PAT7 = 49; + + // TODO(Moto): Need comments for each one and we need ToneGenerator.cpp/ToneGenerator.h + + /** @hide */ + public static final int TONE_CDMA_HIGH_L = 50; + + /** @hide */ + public static final int TONE_CDMA_LOW_L = 51; + /** @hide */ + public static final int TONE_CDMA_HIGH_SS = 52; + /** @hide */ + public static final int TONE_CDMA_MED_SS = 53; + /** @hide */ + public static final int TONE_CDMA_LOW_SS = 54; + /** @hide */ + public static final int TONE_CDMA_HIGH_SSL = 55; + + + /** @hide */ + public static final int TONE_CDMA_MED_SSL = 56; + /** @hide */ + public static final int TONE_CDMA_LOW_SSL = 57; + /** @hide */ + public static final int TONE_CDMA_HIGH_SS_2 = 58; + /** @hide */ + public static final int TONE_CDMA_MED_SS_2 = 59; + /** @hide */ + public static final int TONE_CDMA_LOW_SS_2 = 60; + /** @hide */ + public static final int TONE_CDMA_HIGH_SLS = 61; + /** @hide */ + public static final int TONE_CDMA_MED_SLS = 62; + /** @hide */ + public static final int TONE_CDMA_LOW_SLS = 63; + /** @hide */ + public static final int TONE_CDMA_HIGH_S_X4 = 64; + /** @hide */ + public static final int TONE_CDMA_MED_S_X4 = 65; + /** @hide */ + public static final int TONE_CDMA_LOW_S_X4 = 66; + /** @hide */ + public static final int TONE_CDMA_HIGH_PBX_L = 67; + /** @hide */ + public static final int TONE_CDMA_MED_PBX_L = 68; + /** @hide */ + public static final int TONE_CDMA_LOW_PBX_L = 69; + /** @hide */ + public static final int TONE_CDMA_HIGH_PBX_SS = 70; + /** @hide */ + public static final int TONE_CDMA_MED_PBX_SS = 71; + /** @hide */ + public static final int TONE_CDMA_LOW_PBX_SS = 72; + /** @hide */ + public static final int TONE_CDMA_HIGH_PBX_SSL = 73; + /** @hide */ + public static final int TONE_CDMA_MED_PBX_SSL = 74; + + /** @hide */ + public static final int TONE_CDMA_LOW_PBX_SSL = 75; + /** @hide */ + public static final int TONE_CDMA_HIGH_PBX_SLS = 76; + /** @hide */ + public static final int TONE_CDMA_MED_PBX_SLS = 77; + /** @hide */ + public static final int TONE_CDMA_LOW_PBX_SLS = 78; + /** @hide */ + public static final int TONE_CDMA_HIGH_PBX_S_X4 = 79; + /** @hide */ + public static final int TONE_CDMA_MED_PBX_S_X4 = 80; + /** @hide */ + public static final int TONE_CDMA_LOW_PBX_S_X4 = 81; + /** @hide */ + public static final int TONE_CDMA_INTERCEPT_ONE_SHOT = TONE_SUP_INTERCEPT_ABBREV; + /** @hide */ + public static final int TONE_CDMA_REORDER_ONE_SHOT = TONE_CDMA_ABBR_REORDER; + /** @hide */ + public static final int TONE_CDMA_NETWORK_BUSY_ONE_SHOT = 82; + /** @hide */ + public static final int TONE_CDMA_ABBR_ALERT = 83; + /** @hide */ + public static final int TONE_CDMA_SIGNAL_OFF = 84; + /** @hide */ + public static final int TONE_CDMA_INVALID = 85; + /** Maximum volume, for use with {@link #ToneGenerator(int,int)} */ public static final int MAX_VOLUME = AudioSystem.MAX_VOLUME; /** Minimum volume setting, for use with {@link #ToneGenerator(int,int)} */ @@ -261,10 +499,10 @@ public class ToneGenerator /** * ToneGenerator class contructor specifying output stream type and volume. - * + * * @param streamType The streame type used for tone playback (e.g. STREAM_MUSIC). * @param volume The volume of the tone, given in percentage of maximum volume (from 0-100). - * + * */ public ToneGenerator(int streamType, int volume) { native_setup(streamType, volume); @@ -272,7 +510,7 @@ public class ToneGenerator /** * This method starts the playback of a tone of the specified type. - * only one tone can play at a time: if a tone is playing while this method is called, + * only one tone can play at a time: if a tone is playing while this method is called, * this tone is stopped and replaced by the one requested. * @param toneType The type of tone generate chosen from the following list: *
    @@ -328,9 +566,10 @@ public class ToneGenerator private native final void native_setup(int streamType, int volume); private native final void native_finalize(); + + @Override protected void finalize() { native_finalize(); } + @SuppressWarnings("unused") private int mNativeContext; // accessed by native methods - - } diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index e78422e449de4..dba497250314a 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -54,10 +54,11 @@ public abstract class BaseCommands implements CommandsInterface { protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList(); protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList(); protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList(); - protected RegistrantList mOtaSessionRegistrants = new RegistrantList(); - protected RegistrantList mCallWaitingRegistrants = new RegistrantList(); - protected RegistrantList mInformationRecordsRegistrants = new RegistrantList(); protected Registrant mUnsolOemHookRawRegistrant; + protected RegistrantList mOtaProvisionRegistrants = new RegistrantList(); + protected RegistrantList mCallWaitingInfoRegistrants = new RegistrantList(); + protected RegistrantList mDisplayInfoRegistrants = new RegistrantList(); + protected RegistrantList mSignalInfoRegistrants = new RegistrantList(); protected Registrant mSMSRegistrant; protected Registrant mNITZTimeRegistrant; protected Registrant mSignalStrengthRegistrant; @@ -464,6 +465,29 @@ public abstract class BaseCommands implements CommandsInterface { mRestrictedStateRegistrant.clear(); } + public void registerForDisplayInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mDisplayInfoRegistrants.add(r); + } + + public void unregisterForDisplayInfo(Handler h) { + mDisplayInfoRegistrants.remove(h); + } + + public void registerForCallWaitingInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mCallWaitingInfoRegistrants.add(r); + } + + public void unregisterForCallWaitingInfo(Handler h) { + mCallWaitingInfoRegistrants.remove(h); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + Registrant r = new Registrant (h, what, obj); + mSignalInfoRegistrants.add(r); + } + public void setOnUnsolOemHookRaw(Handler h, int what, Object obj) { mUnsolOemHookRawRegistrant = new Registrant (h, what, obj); } @@ -472,32 +496,19 @@ public abstract class BaseCommands implements CommandsInterface { mUnsolOemHookRawRegistrant.clear(); } - public void registerForOtaSessionStatus(Handler h, int what, Object obj){ + public void unregisterForSignalInfo(Handler h) { + mSignalInfoRegistrants.remove(h); + } + + public void registerForCdmaOtaProvision(Handler h,int what, Object obj){ Registrant r = new Registrant (h, what, obj); - mOtaSessionRegistrants.add(r); + mOtaProvisionRegistrants.add(r); } - public void unregisterForOtaSessionStatus(Handler h){ - mOtaSessionRegistrants.remove(h); + public void unregisterForCdmaOtaProvision(Handler h){ + mOtaProvisionRegistrants.remove(h); } - public void registerForCdmaCallWaiting(Handler h, int what, Object obj){ - Registrant r = new Registrant (h, what, obj); - mCallWaitingRegistrants.add(r); - } - - public void unregisterForCdmaCallWaiting(Handler h){ - mCallWaitingRegistrants.remove(h); - } - - public void registerCdmaInformationRecord(Handler h, int what, Object obj) { - Registrant r = new Registrant (h, what, obj); - mInformationRecordsRegistrants.add(r); - } - - public void unregisterCdmaInformationRecord(Handler h) { - mInformationRecordsRegistrants.remove(h); - } //***** Protected Methods /** diff --git a/telephony/java/com/android/internal/telephony/CallTracker.java b/telephony/java/com/android/internal/telephony/CallTracker.java index eb339f869b8ab..8263ded7a2866 100644 --- a/telephony/java/com/android/internal/telephony/CallTracker.java +++ b/telephony/java/com/android/internal/telephony/CallTracker.java @@ -56,6 +56,7 @@ public abstract class CallTracker extends Handler { protected static final int EVENT_CONFERENCE_RESULT = 11; protected static final int EVENT_SEPARATE_RESULT = 12; protected static final int EVENT_ECT_RESULT = 13; + protected static final int EVENT_EXIT_ECM_RESPONSE_CDMA = 14; protected void pollCallsWhenSafe() { diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index da53e15e93ede..32f31ef4c14bb 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -70,6 +70,12 @@ public class CallerInfo { */ public String name; public String phoneNumber; + + public String cnapName; + public int numberPresentation; + public int namePresentation; + public boolean contactExists; + public String phoneLabel; /* Split up the phoneLabel into number type and label name */ public int numberType; @@ -118,6 +124,7 @@ public class CallerInfo { info.numberLabel = null; info.cachedPhoto = null; info.isCachedPhotoCurrent = false; + info.contactExists = false; if (Config.LOGV) Log.v(TAG, "construct callerInfo from cursor"); @@ -176,6 +183,7 @@ public class CallerInfo { columnIndex = cursor.getColumnIndex(People.SEND_TO_VOICEMAIL); info.shouldSendToVoicemail = (columnIndex != -1) && ((cursor.getInt(columnIndex)) == 1); + info.contactExists = true; } cursor.close(); } @@ -303,4 +311,3 @@ public class CallerInfo { } } } - diff --git a/telephony/java/com/android/internal/telephony/CdmaInformationRecord.java b/telephony/java/com/android/internal/telephony/CdmaInformationRecord.java deleted file mode 100644 index 690df058b59bc..0000000000000 --- a/telephony/java/com/android/internal/telephony/CdmaInformationRecord.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony; - -/** - * TODO(Teleca): This class was poorly implemented and didn't - * follow the Android coding conventions. It is now more or less - * follows the conventions but there is still some work, see the - * TODO's. - */ - - -public class CdmaInformationRecord { - public int messageName; - - public CdmaDisplayInfoRec displayInfoRec; - public CdmaNumberInfoRec numberInfoRec; - public CdmaSignalInfoRec signalInfoRec; - public CdmaRedirectingNumberInfoRec redirectingNumberInfoRec; - public CdmaLineControlInfoRec lineControlInfoRec; - public CdmaT53ClirInfoRec cdmaT53ClirInfoRec; - public CdmaT53AudioControlInfoRec cdmaT53AudioControlInfoRec; - - public static final int RIL_CDMA_DISPLAY_INFO_REC = 0; - public static final int RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC = 1; - public static final int RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC = 2; - public static final int RIL_CDMA_CONNECTED_NUMBER_INFO_REC = 3; - public static final int RIL_CDMA_SIGNAL_INFO_REC = 4; - public static final int RIL_CDMA_REDIRECTING_NUMBER_INFO_REC = 5; - public static final int RIL_CDMA_LINE_CONTROL_INFO_REC = 6; - public static final int RIL_CDMA_EXTENDED_DISPLAY_INFO_REC = 7; - public static final int RIL_CDMA_T53_CLIR_INFO_REC = 8; - public static final int RIL_CDMA_T53_RELEASE_INFO_REC = 9; - public static final int RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC = 10; - - public CdmaInformationRecord(int messageName) { - this.messageName = messageName; - } - - void createDisplayInfo(int length, char buffer[]) { - displayInfoRec = new CdmaDisplayInfoRec(length, buffer); - } - - void createNumberInfo(int length, char buffer[]) { - numberInfoRec = new CdmaNumberInfoRec(length, buffer); - } - - void createSignalInfo(char buffer[]) { - signalInfoRec = new CdmaSignalInfoRec(buffer); - } - - void createRedirectingNumberInfo(int length, char buffer[], int reason) { - redirectingNumberInfoRec = new CdmaRedirectingNumberInfoRec(length, buffer, reason); - } - - void createLineControlInfo(char buffer[]) { - lineControlInfoRec = new CdmaLineControlInfoRec(buffer); - } - - void createT53ClirInfo(char buffer) { - cdmaT53ClirInfoRec = new CdmaT53ClirInfoRec(buffer); - } - - void createT53AudioControlInfo(char ul, char dl) { - cdmaT53AudioControlInfoRec = new CdmaT53AudioControlInfoRec(ul, dl); - } - - /** - * TODO(Teleca): Add comments for each class giving the - * document and section where the information is defined - * as shown CdmaSignalInfoRec. Also add a toString to - * each of these to ease debugging. - */ - - /** - * Signal Information record from 3GPP2 C.S005 3.7.5.5 - */ - public static class CdmaSignalInfoRec { - public boolean isPresent; /* non-zero if signal information record is present */ - public int signalType; - public int alertPitch; - public int signalCode; - - public CdmaSignalInfoRec() {} - - public CdmaSignalInfoRec(char buffer[]) { - isPresent = buffer[0] == 1; - signalType = buffer[1]; - alertPitch = buffer[2]; - signalCode = buffer[3]; - } - - @Override - public String toString() { - return "CdmaSignalInfo: {" + - " isPresent: " + isPresent + - ", signalType: " + signalType + - ", alertPitch: " + alertPitch + - ", signalCode: " + signalCode + - " }"; - } - } - - public static class CdmaDisplayInfoRec { - public char alphaLen; - public char alphaBuf[]; - - public CdmaDisplayInfoRec(int length, char buffer[]) { - alphaLen = (char)length; - alphaBuf = new char[length]; - for(int i = 0; i < length; i++) - alphaBuf[i] = buffer[i]; - } - } - - public static class CdmaNumberInfoRec { - public int len; - public char buf[]; - public char numberType; - public char numberPlan; - public char pi; // TODO(Teleca): poor name, no meaning - public char si; // TODO(Teleca): poor name - - public CdmaNumberInfoRec(int length, char buffer[]) { - int i; - - len = length; - buf = new char[length]; - for (i = 0; i < len; i++) { - buf[i] = buffer[i]; - } - - numberType = buffer[i++]; - numberPlan = buffer[i++]; - pi = buffer[i++]; - si = buffer[i++]; - } - } - - public static class CdmaRedirectingNumberInfoRec { - public static final int REASON_UNKNOWN = 0; - public static final int REASON_CALL_FORWARDING_BUSY = 1; - public static final int REASON_CALL_FORWARDING_NO_REPLY = 2; - public static final int REASON_CALLED_DTE_OUT_OF_ORDER = 9; - public static final int REASON_CALL_FORWARDING_BY_THE_CALLED_DTE = 10; - public static final int REASON_CALL_FORWARDING_UNCONDITIONAL = 15; - - public CdmaNumberInfoRec numberInfoRec; - public int redirectingReason; - - public CdmaRedirectingNumberInfoRec(int length, char buffer[], int reason) { - numberInfoRec = new CdmaNumberInfoRec(length, buffer); - redirectingReason = reason; - } - } - - public static class CdmaLineControlInfoRec { - public char lineCtrlPolarityIncluded; - public char lineCtrlToggle; - public char lineCtrlReverse; - public char lineCtrlPowerDenial; - - CdmaLineControlInfoRec(char buffer[]) { - lineCtrlPolarityIncluded = buffer[0]; - lineCtrlToggle = buffer[1]; - lineCtrlReverse = buffer[2]; - lineCtrlPowerDenial = buffer[3]; - } - } - - // TODO(Teleca): A class for a single character, is this needed? - public static class CdmaT53ClirInfoRec { - public char cause; - - public CdmaT53ClirInfoRec(char buffer) { - cause = buffer; - } - } - - public static class CdmaT53AudioControlInfoRec { - public char uplink; - public char downlink; - - public CdmaT53AudioControlInfoRec(char ul, char dl) { - uplink = ul; - downlink = dl; - } - } -} diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index ddf6b50f6f203..94a1c13ab6bb8 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -240,24 +240,6 @@ public interface CommandsInterface { void registerForRUIMReady(Handler h, int what, Object obj); void unregisterForRUIMReady(Handler h); - /** - * Registers for the status of an OTASP/OTAPA session - */ - void registerForOtaSessionStatus(Handler h, int what, Object obj); - void unregisterForOtaSessionStatus(Handler h); - - /** - * register for Call waiting for CDMA - */ - void registerForCdmaCallWaiting(Handler h, int what, Object obj); - void unregisterForCdmaCallWaiting(Handler h); - - /** - * Registers for CDMA information records - */ - void registerCdmaInformationRecord(Handler h, int what, Object obj); - void unregisterCdmaInformationRecord(Handler h); - /** * unlike the register* methods, there's only one new SMS handler * if you need to unregister, you should also tell the radio to stop @@ -343,16 +325,6 @@ public interface CommandsInterface { void setOnIccSmsFull(Handler h, int what, Object obj); void unSetOnIccSmsFull(Handler h); - /** - * Sets the handler for Emergency call-back Mode enter mesage. - * Unlike the register* methods, there's only one notification handler - * - * @param h Handler for notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void setEmergencyCallbackMode(Handler h, int what, Object obj); - /** * Sets the handler for SIM Refresh notifications. * Unlike the register* methods, there's only one notification handler @@ -452,6 +424,49 @@ public interface CommandsInterface { void setSuppServiceNotifications(boolean enable, Message result); //void unSetSuppServiceNotifications(Handler h); + /** + * Sets the handler for Event Notifications for CDMA Display Info. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForDisplayInfo(Handler h, int what, Object obj); + void unregisterForDisplayInfo(Handler h); + + /** + * Sets the handler for Event Notifications for CallWaiting Info. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForCallWaitingInfo(Handler h, int what, Object obj); + void unregisterForCallWaitingInfo(Handler h); + + /** + * Sets the handler for Event Notifications for Signal Info. + * Unlike the register* methods, there's only one notification handler + * + * @param h Handler for notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForSignalInfo(Handler h, int what, Object obj); + void unregisterForSignalInfo(Handler h); + + /** + * Fires on if Modem enters Emergency Callback mode + */ + void setEmergencyCallbackMode(Handler h, int what, Object obj); + + /** + * Fires on any CDMA OTA provision status change + */ + void registerForCdmaOtaProvision(Handler h,int what, Object obj); + void unregisterForCdmaOtaProvision(Handler h); /** * Returns current ICC status. @@ -1244,7 +1259,9 @@ public interface CommandsInterface { public void getCdmaBroadcastConfig(Message result); /** - * Requests the radio's system selection module to exit emergency callback mode. + * Requests the radio's system selection module to exit emergency callback mode. + * This function should only be called from CDMAPHone.java. + * * @param response callback message */ public void exitEmergencyCallbackMode(Message response); diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java index c6bbf82fd308e..237974d0ce25d 100644 --- a/telephony/java/com/android/internal/telephony/Connection.java +++ b/telephony/java/com/android/internal/telephony/Connection.java @@ -72,6 +72,31 @@ public abstract class Connection { public abstract String getAddress(); + /** + * Gets cdma CNAP name associated with connection + * @return cnap name or null if unavailable + */ + public String getCnapName() { + return null; + } + + /** + * Get orignal dial string + * @return orignal dial string or null if unavailable + */ + public String getOrigDialString(){ + return null; + } + + /** + * Gets cdma CNAP presentation associated with connection + * @return cnap name or null if unavailable + */ + + public int getCnapNamePresentation() { + return 0; + }; + /** * @return Call that owns this Connection, or null if none */ @@ -204,8 +229,14 @@ public abstract class Connection { WILD, /* The post dial string playback is waiting for a call to proceedAfterWildChar() */ COMPLETE, /* The post dial string playback is complete */ - CANCELLED /* The post dial string playback was cancelled + CANCELLED, /* The post dial string playback was cancelled with cancelPostDial() */ + PAUSE /* The post dial string playback is pausing for a + call to processNextPostDialChar*/ + } + + public void clearUserData(){ + userData = null; } public abstract PostDialState getPostDialState(); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index cc6b452e54c81..6e6f64c160375 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -238,5 +238,10 @@ interface ITelephony { */ String getCdmaEriText(); + /** + * Returns the unread count of voicemails + */ + int getCountVoiceMessages(); + } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 4d0cf41341c4d..260f662452097 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -102,6 +102,7 @@ public interface Phone { static final String DATA_APN_KEY = "apn"; static final String DATA_IFACE_NAME_KEY = "iface"; static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable"; + static final String PHONE_IN_ECM_STATE = "phoneinECMState"; /** * APN types for data connections. These are usage categories for an APN @@ -195,6 +196,24 @@ public interface Phone { static final int TTY_MODE_HCO = 2; static final int TTY_MODE_VCO = 3; + /** + * CDMA OTA PROVISION STATUS, the same as RIL_CDMA_OTA_Status in ril.h + */ + + public static final int CDMA_OTA_PROVISION_STATUS_SPL_UNLOCKED = 0; + public static final int CDMA_OTA_PROVISION_STATUS_SPC_RETRIES_EXCEEDED = 1; + public static final int CDMA_OTA_PROVISION_STATUS_A_KEY_EXCHANGED = 2; + public static final int CDMA_OTA_PROVISION_STATUS_SSD_UPDATED = 3; + public static final int CDMA_OTA_PROVISION_STATUS_NAM_DOWNLOADED = 4; + public static final int CDMA_OTA_PROVISION_STATUS_MDN_DOWNLOADED = 5; + public static final int CDMA_OTA_PROVISION_STATUS_IMSI_DOWNLOADED = 6; + public static final int CDMA_OTA_PROVISION_STATUS_PRL_DOWNLOADED = 7; + public static final int CDMA_OTA_PROVISION_STATUS_COMMITTED = 8; + public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_STARTED = 9; + public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED = 10; + public static final int CDMA_OTA_PROVISION_STATUS_OTAPA_ABORTED = 11; + + /** * Get the current ServiceState. Use * registerForServiceStateChanged to be informed of @@ -273,7 +292,12 @@ public interface Phone { String getActiveApn(); /** - * Get current signal strength. + * Get current signal strength. No change notification available on this + * interface. Use PhoneStateNotifier or an equivalent. + * An ASU is 0-31 or -1 if unknown (for GSM, dBm = -113 - 2 * asu). + * The following special values are defined:

    + *
    • 0 means "-113 dBm or less".
    • + *
    • 31 means "-51 dBm or greater".
    * * @return Current signal strength as SignalStrength */ @@ -499,52 +523,20 @@ public interface Phone { void unregisterForInCallVoicePrivacyOff(Handler h); /** - * Register for notifications about information record available + * Register for notifications when CDMA OTA Provision status change * * @param h Handler that receives the notification message. * @param what User-defined message code. * @param obj User object. */ - void registerCdmaInformationRecord(Handler h, int what, Object obj); + void registerForCdmaOtaStatusChange(Handler h, int what, Object obj); /** - * Unregister for notifications about information record available - * + * Unegister for notifications when CDMA OTA Provision status change * @param h Handler to be removed from the registrant list. */ - void unregisterCdmaInformationRecord(Handler h); + void unregisterForCdmaOtaStatusChange(Handler h); - /** - * Register for the indication of OTA status change - * - * @param h Handler that receives the notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void registerForOtaStatusChange(Handler h, int what, Object obj); - - /** - * Unregister for the indication of OTA status change - * - * @param h Handler to be removed from the registrant list. - */ - void unregisterForOtaStatusChange(Handler h); - - /** - * Register for the indication of Cdma Call Waiting - * - * @param h Handler that receives the notification message. - * @param what User-defined message code. - * @param obj User object. - */ - void registerForCdmaCallWaiting(Handler h, int what, Object obj); - - /** - * Unregister for the indication of Cdma Call Waiting - * - * @param h Handler to be removed from the registrant list. - */ - void unregisterForCdmaCallWaiting(Handler h); /** * Returns SIM record load state. Use * getSimCard().registerForReady() for change notification. @@ -760,9 +752,18 @@ public interface Phone { void stopDtmf(); /** - * Play a Burst of DTMF tone on the active call. Ignored if there is no active call. + * send burst DTMF tone, it can send the string as single character or multiple character + * ignore if there is no active call or not valid digits string. + * Valid digit means only includes characters ISO-LATIN characters 0-9, *, # + * The difference between sendDtmf and sendBurstDtmf is sendDtmf only sends one character, + * this api can send single character and multiple character, also, this api has response + * back to caller. + * + * @param dtmfString is string representing the dialing digit(s) in the active call + * @param onCompelte is the callback message when the action is processed by BP + * */ - void sendBurstDtmf(String dtmfString); + void sendBurstDtmf(String dtmfString, Message onComplete); /** * Sets the radio power on/off state (off is sometimes @@ -826,6 +827,12 @@ public interface Phone { */ String getVoiceMailNumber(); + /** + * Returns unread voicemail count. This count is shown when the voicemail + * notification is expanded.

    + */ + int getCountVoiceMessages(); + /** * Returns the alpha tag associated with the voice mail number. * If there is no alpha tag associated or the record is not yet available, @@ -859,7 +866,7 @@ public interface Phone { * * @param commandInterfaceCFReason is one of the valid call forwarding * CF_REASONS, as defined in - * com.android.internal.telephony.CommandsInterface./code> + * com.android.internal.telephony.CommandsInterface. * @param onComplete a callback message when the action is completed. * @see com.android.internal.telephony.CallForwardInfo for details. */ @@ -872,10 +879,10 @@ public interface Phone { * * @param commandInterfaceCFReason is one of the valid call forwarding * CF_REASONS, as defined in - * com.android.internal.telephony.CommandsInterface./code> + * com.android.internal.telephony.CommandsInterface. * @param commandInterfaceCFAction is one of the valid call forwarding * CF_ACTIONS, as defined in - * com.android.internal.telephony.CommandsInterface./code> + * com.android.internal.telephony.CommandsInterface. * @param dialingNumber is the target phone number to forward calls to * @param timerSeconds is used by CFNRy to indicate the timeout before * forwarding is attempted. @@ -1335,10 +1342,16 @@ public interface Phone { //***** CDMA support methods + /* + * TODO(Moto) TODO(Teleca): can getCdmaMin, getEsn, getMeid use more generic calls + * already defined getXxxx above? + */ + /** * Retrieves the MIN for CDMA phones. */ - String getMin(); + + String getCdmaMin(); /** * Retrieves the ESN for CDMA phones. @@ -1383,14 +1396,6 @@ public interface Phone { */ void queryTTYMode(Message onComplete); - /** - * exitEmergencyCallbackMode - * exits the emergency callback mode - * - * @param onComplete a callback message when the action is completed. - */ - void exitEmergencyCallbackMode(Message onComplete); - /** * Activate or deactivate cell broadcast SMS. * @@ -1438,4 +1443,93 @@ public interface Phone { */ public String getCdmaEriText(); + /** + * request to exit emergency call back mode + * the caller should use setOnECMModeExitResponse + * to receive the emergency callback mode exit response + */ + void exitEmergencyCallbackMode(); + + /** + * this decides if the dial number is OTA(Over the air provision) number or not + * @param dialStr is string representing the dialing digit(s) + * @return true means the dialStr is OTA number, and false means the dialStr is not OTA number + */ + boolean isOtaSpNumber(String dialStr); + + /** + * Register for notifications when CDMA call waiting comes + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + // TODO(Moto) TODO: Remove when generic implemented + void registerForCallWaiting(Handler h, int what, Object obj); + + /** + * Unegister for notifications when CDMA Call waiting comes + * @param h Handler to be removed from the registrant list. + */ + // TODO(Moto): Remove when generic implemented + void unregisterForCallWaiting(Handler h); + + + /** + * Register for signal information notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + + void registerForSignalInfo(Handler h, int what, Object obj) ; + /** + * Unregisters for signal information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForSignalInfo(Handler h); + + /** + * Register for display information notifications from the network. + * Message.obj will contain an AsyncResult. + * AsyncResult.result will be a SuppServiceNotification instance. + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + void registerForDisplayInfo(Handler h, int what, Object obj); + + /** + * Unregisters for display information notifications. + * Extraneous calls are tolerated silently + * + * @param h Handler to be removed from the registrant list. + */ + void unregisterForDisplayInfo(Handler h) ; + + + /** + * registers for exit emergency call back mode request response + * + * @param h Handler that receives the notification message. + * @param what User-defined message code. + * @param obj User object. + */ + + void setOnEcbModeExitResponse(Handler h, int what, Object obj); + + /** + * Unregisters for exit emergency call back mode request response + * + * @param h Handler to be removed from the registrant list. + */ + void unsetOnEcbModeExitResponse(Handler h); + + } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 7234aa39283c1..d856279d89c33 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -100,8 +100,8 @@ public abstract class PhoneBase implements Phone { protected static final int EVENT_RUIM_RECORDS_LOADED = 21; protected static final int EVENT_NV_READY = 22; protected static final int EVENT_SET_ENHANCED_VP = 23; - protected static final int EVENT_CDMA_CALL_WAITING = 24; - protected static final int EVENT_EMERGENCY_CALLBACK_MODE = 25; + protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 24; + protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 25; // Key used to read/write current CLIR setting public static final String CLIR_KEY = "clir_key"; @@ -294,36 +294,6 @@ public abstract class PhoneBase implements Phone { mCM.unregisterForInCallVoicePrivacyOff(h); } - // Inherited documentation suffices. - public void registerForOtaStatusChange(Handler h, int what, Object obj){ - mCM.registerForOtaSessionStatus(h,what,obj); - } - - // Inherited documentation suffices. - public void unregisterForOtaStatusChange(Handler h){ - mCM.unregisterForOtaSessionStatus(h); - } - - // Inherited documentation suffices. - public void registerCdmaInformationRecord(Handler h, int what, Object obj){ - mCM.registerCdmaInformationRecord(h,what,obj); - } - - // Inherited documentation suffices. - public void unregisterCdmaInformationRecord(Handler h){ - mCM.unregisterCdmaInformationRecord(h); - } - - // Inherited documentation suffices. - public void registerForCdmaCallWaiting(Handler h, int what, Object obj){ - mCM.registerForCdmaCallWaiting(h,what,obj); - } - - // Inherited documentation suffices. - public void unregisterForCdmaCallWaiting(Handler h){ - mCM.unregisterForCdmaCallWaiting(h); - } - /** * Notifiy registrants of a new ringing Connection. * Subclasses of Phone probably want to replace this with a @@ -630,14 +600,6 @@ public abstract class PhoneBase implements Phone { Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); } - /** - * Send the exit emergency callback mode message - */ - public void exitEmergencyCallbackMode(Message onComplete) { - // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. - Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); - } - /** * This should only be called in GSM mode. * Only here for some backward compatibility @@ -684,6 +646,11 @@ public abstract class PhoneBase implements Phone { public abstract String getPhoneName(); + /** @hide */ + public int getCountVoiceMessages(){ + return 0; + } + /** * Returns the CDMA ERI icon index to display */ @@ -710,4 +677,71 @@ public abstract class PhoneBase implements Phone { return "GSM nw, no ERI"; } + public String getCdmaMin() { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + return null; + } + + public void sendBurstDtmf(String dtmfString, Message onComplete) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void exitEmergencyCallbackMode() { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void unregisterForCdmaOtaStatusChange(Handler h) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public boolean isOtaSpNumber(String dialStr) { + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + return false; + } + + public void registerForCallWaiting(Handler h, int what, Object obj){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void unregisterForCallWaiting(Handler h){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + mCM.registerForSignalInfo(h, what, obj); + } + + public void unregisterForSignalInfo(Handler h) { + mCM.unregisterForSignalInfo(h); + } + + public void registerForDisplayInfo(Handler h, int what, Object obj) { + mCM.registerForDisplayInfo(h, what, obj); + } + + public void unregisterForDisplayInfo(Handler h) { + mCM.unregisterForDisplayInfo(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } + + public void unsetOnEcbModeExitResponse(Handler h){ + // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. + Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); + } } diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index a329cbbaac2c1..7d968f97e5e06 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -306,28 +306,12 @@ public class PhoneProxy extends Handler implements Phone { mActivePhone.unregisterForInCallVoicePrivacyOff(h); } - public void registerCdmaInformationRecord(Handler h, int what, Object obj) { - mActivePhone.registerCdmaInformationRecord(h,what,obj); + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + mActivePhone.registerForCdmaOtaStatusChange(h,what,obj); } - public void unregisterCdmaInformationRecord(Handler h) { - mActivePhone.unregisterCdmaInformationRecord(h); - } - - public void registerForOtaStatusChange(Handler h, int what, Object obj){ - mActivePhone.registerForOtaStatusChange(h,what,obj); - } - - public void unregisterForOtaStatusChange(Handler h){ - mActivePhone.unregisterForOtaStatusChange(h); - } - - public void registerForCdmaCallWaiting(Handler h, int what, Object obj){ - mActivePhone.registerForCdmaCallWaiting(h,what,obj); - } - - public void unregisterForCdmaCallWaiting(Handler h){ - mActivePhone.unregisterForCdmaCallWaiting(h); + public void unregisterForCdmaOtaStatusChange(Handler h) { + mActivePhone.unregisterForCdmaOtaStatusChange(h); } public boolean getIccRecordsLoaded() { @@ -414,10 +398,6 @@ public class PhoneProxy extends Handler implements Phone { mActivePhone.stopDtmf(); } - public void sendBurstDtmf(String dtmfString) { - mActivePhone.sendBurstDtmf(dtmfString); - } - public void setRadioPower(boolean power) { mActivePhone.setRadioPower(power); } @@ -434,6 +414,10 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getLine1Number(); } + public String getCdmaMin() { + return mActivePhone.getCdmaMin(); + } + public String getLine1AlphaTag() { return mActivePhone.getLine1AlphaTag(); } @@ -446,6 +430,11 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getVoiceMailNumber(); } + /** @hide */ + public int getCountVoiceMessages(){ + return mActivePhone.getCountVoiceMessages(); + } + public String getVoiceMailAlphaTag() { return mActivePhone.getVoiceMailAlphaTag(); } @@ -656,10 +645,6 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getIccSerialNumber(); } - public String getMin() { - return mActivePhone.getMin(); - } - public String getEsn() { return mActivePhone.getEsn(); } @@ -688,10 +673,6 @@ public class PhoneProxy extends Handler implements Phone { mActivePhone.queryTTYMode(onComplete); } - public void exitEmergencyCallbackMode(Message onComplete) { - mActivePhone.exitEmergencyCallbackMode(onComplete); - } - public void activateCellBroadcastSms(int activate, Message response) { mActivePhone.activateCellBroadcastSms(activate, response); } @@ -720,12 +701,55 @@ public class PhoneProxy extends Handler implements Phone { return mActivePhone.getCdmaEriIconIndex(); } + public String getCdmaEriText() { + return mActivePhone.getCdmaEriText(); + } + public int getCdmaEriIconMode() { return mActivePhone.getCdmaEriIconMode(); } - public String getCdmaEriText() { - return mActivePhone.getCdmaEriText(); + public void sendBurstDtmf(String dtmfString, Message onComplete){ + mActivePhone.sendBurstDtmf(dtmfString,onComplete); + } + + public void exitEmergencyCallbackMode(){ + mActivePhone.exitEmergencyCallbackMode(); + } + + public boolean isOtaSpNumber(String dialStr){ + return mActivePhone.isOtaSpNumber(dialStr); + } + + public void registerForCallWaiting(Handler h, int what, Object obj){ + mActivePhone.registerForCallWaiting(h,what,obj); + } + + public void unregisterForCallWaiting(Handler h){ + mActivePhone.unregisterForCallWaiting(h); + } + + public void registerForSignalInfo(Handler h, int what, Object obj) { + mActivePhone.registerForSignalInfo(h,what,obj); + } + + public void unregisterForSignalInfo(Handler h) { + mActivePhone.unregisterForSignalInfo(h); + } + + public void registerForDisplayInfo(Handler h, int what, Object obj) { + mActivePhone.registerForDisplayInfo(h,what,obj); + } + + public void unregisterForDisplayInfo(Handler h) { + mActivePhone.unregisterForDisplayInfo(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj){ + mActivePhone.setOnEcbModeExitResponse(h,what,obj); + } + + public void unsetOnEcbModeExitResponse(Handler h){ + mActivePhone.unsetOnEcbModeExitResponse(h); } } - diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 070d233bbdb66..792e67fda7b5e 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -38,11 +38,18 @@ import android.telephony.SmsMessage; import android.util.Config; import android.util.Log; -import com.android.internal.telephony.CdmaInformationRecord; -import com.android.internal.telephony.cdma.CdmaCallWaiting; +import com.android.internal.telephony.CallForwardInfo; +import com.android.internal.telephony.CommandException; import com.android.internal.telephony.DataCallState; import com.android.internal.telephony.gsm.NetworkInfo; import com.android.internal.telephony.gsm.SuppServiceNotification; +import com.android.internal.telephony.IccCardApplication; +import com.android.internal.telephony.IccCardStatus; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.RILConstants; +import com.android.internal.telephony.SmsResponse; +import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; +import com.android.internal.telephony.cdma.CdmaInformationRecords; import java.io.ByteArrayInputStream; import java.io.DataInputStream; @@ -1063,10 +1070,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { sendBurstDtmf(String dtmfString, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BURST_DTMF, result); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); - rr.mp.writeString(dtmfString); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + dtmfString); + send(rr); } @@ -1992,7 +2000,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_CONFERENCE: ret = responseVoid(p); break; case RIL_REQUEST_UDUB: ret = responseVoid(p); break; case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret = responseInts(p); break; - case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseInts(p); break; + case RIL_REQUEST_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break; case RIL_REQUEST_REGISTRATION_STATE: ret = responseStrings(p); break; case RIL_REQUEST_GPRS_REGISTRATION_STATE: ret = responseStrings(p); break; case RIL_REQUEST_OPERATOR: ret = responseStrings(p); break; @@ -2187,7 +2195,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret = responseInts(p); break; case RIL_UNSOL_ON_USSD: ret = responseStrings(p); break; case RIL_UNSOL_NITZ_TIME_RECEIVED: ret = responseString(p); break; - case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseInts(p); break; + case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break; case RIL_UNSOL_DATA_CALL_LIST_CHANGED: ret = responseDataCallList(p);break; case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break; case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break; @@ -2205,7 +2213,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break; case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: ret = responseInts(p); break; - case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInformationRecord(p); break; + case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInfoRec(p); break; case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break; default: @@ -2391,10 +2399,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { break; case RIL_UNSOL_CALL_RING: - if (RILJ_LOGD) unsljLog(response); + if (RILJ_LOGD) unsljLogRet(response, ret); if (mRingRegistrant != null) { - mRingRegistrant.notifyRegistrant(); + mRingRegistrant.notifyRegistrant( + new AsyncResult (null, ret, null)); } break; @@ -2434,13 +2443,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { } break; - case RIL_UNSOL_OEM_HOOK_RAW: - if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret)); - if (mUnsolOemHookRawRegistrant != null) { - mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null)); - } - break; - case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: if (RILJ_LOGD) unsljLog(response); @@ -2452,25 +2454,46 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_CDMA_CALL_WAITING: if (RILJ_LOGD) unsljLog(response); - if(mCallWaitingRegistrants != null) { - mCallWaitingRegistrants.notifyRegistrants(new AsyncResult (null, ret, null)); + if (mCallWaitingInfoRegistrants != null) { + mCallWaitingInfoRegistrants.notifyRegistrants( + new AsyncResult (null, ret, null)); } break; case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: - if (RILJ_LOGD) unsljLog(response); + if (RILJ_LOGD) unsljLogRet(response, ret); - if (mOtaSessionRegistrants != null) { - mOtaSessionRegistrants.notifyRegistrants(new AsyncResult(null, ret, null)); + if (mOtaProvisionRegistrants != null) { + mOtaProvisionRegistrants.notifyRegistrants( + new AsyncResult (null, ret, null)); } break; case RIL_UNSOL_CDMA_INFO_REC: - if (RILJ_LOGD) - unsljLog(response); - if (mInformationRecordsRegistrants != null) { - mInformationRecordsRegistrants.notifyRegistrants(new AsyncResult(null, ret, - null)); + if (RILJ_LOGD) unsljLog(response); + + CdmaInformationRecords infoRec = (CdmaInformationRecords) ret; + if (infoRec.isDispInfo) { + if (mDisplayInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.cdmaDisplayInfoRecord); + + mDisplayInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.cdmaDisplayInfoRecord, null)); + } + } + if (infoRec.isSignInfo) { + if (mSignalInfoRegistrants != null) { + if (RILJ_LOGD) unsljLogRet(response, infoRec.cdmaSignalInfoRecord); + mSignalInfoRegistrants.notifyRegistrants( + new AsyncResult (null, infoRec.cdmaSignalInfoRecord, null)); + } + } + break; + + case RIL_UNSOL_OEM_HOOK_RAW: + if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret)); + if (mUnsolOemHookRawRegistrant != null) { + mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null)); } break; } @@ -2721,11 +2744,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { dc.als = p.readInt(); voiceSettings = p.readInt(); dc.isVoice = (0 == voiceSettings) ? false : true; - - //dc.isVoicePrivacy = (0 != p.readInt()); int voicePrivacy = p.readInt(); dc.isVoicePrivacy = (0 != voicePrivacy); - dc.number = p.readString(); int np = p.readInt(); dc.numberPresentation = DriverCall.presentationFromCLIP(np); @@ -2834,15 +2854,48 @@ public final class RIL extends BaseCommands implements CommandsInterface { private Object responseCDMA_BR_CNF(Parcel p) { - int numInts; + int numServiceCategories; int response[]; - numInts = p.readInt(); + numServiceCategories = p.readInt(); + if (numServiceCategories == 0) { + int numInts; + numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES * CDMA_BSI_NO_OF_INTS_STRUCT + 1; + response = new int[numInts]; + + // Indicate that a zero length table was received + response[0] = 0; // TODO(Moto): This is very strange, please explain why. + + // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as + // default language and selection status to false + for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT ) { + response[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT; + response[i + 1] = 1; + response[i + 2] = 0; + } + } else { + int numInts; + numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1; + response = new int[numInts]; + + response[0] = numServiceCategories; + for (int i = 1 ; i < numInts; i++) { + response[i] = p.readInt(); + } + } + + return response; + } + + private Object + responseSignalStrength(Parcel p) { + int numInts = 7; + int response[]; + + /* TODO: Add SignalStrength class to match RIL_SignalStrength */ response = new int[numInts]; - - response[0] = numInts; - for (int i = 1 ; i < numInts; i++) { + for (int i = 0 ; i < numInts ; i++) { response[i] = p.readInt(); } @@ -2850,105 +2903,81 @@ public final class RIL extends BaseCommands implements CommandsInterface { } private Object - responseCdmaInformationRecord(Parcel p){ + responseCdmaInfoRec(Parcel p) { + int infoRecordName; + CdmaInformationRecords records = new CdmaInformationRecords(); - int num; - ArrayList response; - - num = p.readInt(); - - response = new ArrayList(num); - - for (int i = 0; i < num; i++) { - int name = p.readInt(); - CdmaInformationRecord InfoRec = new CdmaInformationRecord(name); - - char buffer[]; - int length; - switch (name) { - case CdmaInformationRecord.RIL_CDMA_DISPLAY_INFO_REC: - case CdmaInformationRecord.RIL_CDMA_EXTENDED_DISPLAY_INFO_REC: - length = p.readInt(); - buffer = new char[length]; - for (int j = 0; j < length; j++) { - buffer[j] = (char)p.readInt(); - } - InfoRec.createDisplayInfo(length, buffer); + int numberOfInfoRecs = p.readInt(); + for (int i = 0; i < numberOfInfoRecs; i++) { + infoRecordName = p.readInt(); + switch(infoRecordName) { + case CdmaInformationRecords.RIL_CDMA_DISPLAY_INFO_REC: + case CdmaInformationRecords.RIL_CDMA_EXTENDED_DISPLAY_INFO_REC: + records.setDispInfo(p.readString()); break; - - case CdmaInformationRecord.RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC: - case CdmaInformationRecord.RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC: - case CdmaInformationRecord.RIL_CDMA_CONNECTED_NUMBER_INFO_REC: - length = p.readInt(); - buffer = new char[length + 4]; - for (int j = 0; j < (length + 4); j++) { - buffer[j] = (char)p.readInt(); - } - InfoRec.createNumberInfo(length, buffer); + case CdmaInformationRecords.RIL_CDMA_SIGNAL_INFO_REC: + records.setSignInfo(p.readInt(), p.readInt(), p.readInt(), p.readInt()); break; - - case CdmaInformationRecord.RIL_CDMA_SIGNAL_INFO_REC: - buffer = new char[4]; - for (int j = 0; j < 4; j++) { - buffer[j] = (char)p.readInt(); - } - InfoRec.createSignalInfo(buffer); + // InfoReocords with names as below aren't supported in AFW yet + case CdmaInformationRecords.RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC: + case CdmaInformationRecords.RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC: + case CdmaInformationRecords.RIL_CDMA_CONNECTED_NUMBER_INFO_REC: + // TODO(Moto) implement + p.readString(); // number + p.readInt(); // number_type + p.readInt(); // number_plan + p.readInt(); // pi + p.readInt(); // si break; - - case CdmaInformationRecord.RIL_CDMA_REDIRECTING_NUMBER_INFO_REC: - length = p.readInt(); - buffer = new char[length + 4]; - int reason; - for (int j = 0; j < (length + 4); j++) { - buffer[j] = (char)p.readInt(); - } - reason = p.readInt(); - InfoRec.createRedirectingNumberInfo(length, buffer, reason); + case CdmaInformationRecords.RIL_CDMA_REDIRECTING_NUMBER_INFO_REC: + // TODO(Moto) implement + p.readString(); // redirecting number + p.readInt(); // number_type + p.readInt(); // number_plan + p.readInt(); // pi + p.readInt(); // si + p.readInt(); // reason break; - - case CdmaInformationRecord.RIL_CDMA_LINE_CONTROL_INFO_REC: - buffer = new char[4]; - for (int j = 0; j < 4; j++) { - buffer[j] = (char)p.readInt(); - } - InfoRec.createLineControlInfo(buffer); + case CdmaInformationRecords.RIL_CDMA_LINE_CONTROL_INFO_REC: + // TODO(Moto) implement + p.readInt(); // PolarityIncluded + p.readInt(); // Toggle + p.readInt(); // Reverse + p.readInt(); // PowerDenial break; - - case CdmaInformationRecord.RIL_CDMA_T53_CLIR_INFO_REC: - char ch = (char)p.readInt(); - InfoRec.createT53ClirInfo(ch); + case CdmaInformationRecords.RIL_CDMA_T53_CLIR_INFO_REC: + // TODO(Moto) implement + p.readInt(); // Cause break; - - case CdmaInformationRecord.RIL_CDMA_T53_RELEASE_INFO_REC: + case CdmaInformationRecords.RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC: + // TODO(Moto) implement + p.readInt(); // upLink + p.readInt(); // downLink break; - - case CdmaInformationRecord.RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC: - char ul = (char)p.readInt(); - char dl = (char)p.readInt(); - InfoRec.createT53AudioControlInfo(ul, dl); - break; - + case CdmaInformationRecords.RIL_CDMA_T53_RELEASE_INFO_REC: + // TODO(Moto) implement unknown fall through default: - break; + throw new RuntimeException("RIL_UNSOL_CDMA_INFO_REC: unsupported record. Got " + + records.recordToString(infoRecordName) + " "); } - response.add(InfoRec); } - - return response; + return records; } private Object - responseCdmaCallWaiting(Parcel p){ - CdmaCallWaiting response = new CdmaCallWaiting(); - response.number = p.readString(); - response.numberPresentation = p.readInt(); - response.name = p.readString(); - response.signalInfoRecord.isPresent = p.readInt() == 0 ? false : true; - response.signalInfoRecord.signalType = p.readInt(); - response.signalInfoRecord.alertPitch = p.readInt(); - response.signalInfoRecord.signalCode = p.readInt(); + responseCdmaCallWaiting(Parcel p) { + CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification(); - return response; + notification.number = p.readString(); + notification.numberPresentation = p.readInt(); + notification.name = p.readString(); + notification.namePresentation = notification.numberPresentation; + notification.isPresent = p.readInt(); + notification.signalType = p.readInt(); + notification.alertPitch = p.readInt(); + notification.signal = p.readInt(); + + return notification; } private Object @@ -3104,16 +3133,16 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL"; case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH"; case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING"; - case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "RIL_UNSOL_RESTRICTED_STATE_CHANGED"; - case RIL_UNSOL_OEM_HOOK_RAW: return "RIL_UNSOL_OEM_HOOK_RAW"; case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: return "UNSOL_RESPONSE_SIM_STATUS_CHANGED"; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: return "UNSOL_RESPONSE_CDMA_NEW_SMS"; case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: return "UNSOL_RESPONSE_NEW_BROADCAST_SMS"; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL"; + case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "UNSOL_RESTRICTED_STATE_CHANGED"; case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE"; case RIL_UNSOL_CDMA_CALL_WAITING: return "UNSOL_CDMA_CALL_WAITING"; case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS"; case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC"; + case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW"; default: return ""; } } @@ -3286,8 +3315,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { * {@inheritDoc} */ public void exitEmergencyCallbackMode(Message response) { - RILRequest rr - = RILRequest.obtain(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, response); + RILRequest rr = RILRequest.obtain(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, response); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 91aa34e79b232..c78ceaec49601 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -54,8 +54,10 @@ public class TelephonyIntents { public static final String ACTION_RADIO_TECHNOLOGY_CHANGED = "android.intent.action.RADIO_TECHNOLOGY"; /** - *

    Broadcast Action: The emergency callback mode is entered. - * + *

    Broadcast Action: The emergency callback mode is changed. + *

      + *
    • phoneinECMState - A boolean value,true=phone in ECM, false=ECM off
    • + *
    *

    * You can not receive this through components declared * in manifests, only by explicitly registering for it with @@ -65,8 +67,8 @@ public class TelephonyIntents { *

    * Requires no permission. */ - public static final String ACTION_EMERGENCY_CALLBACK_MODE_ENTERED - = "android.intent.action.EMERGENCY_CALLBACK_MODE"; + public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED + = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED"; /** * Broadcast Action: The phone's signal strength has changed. The intent will have the * following extra values:

    @@ -179,4 +181,28 @@ public class TelephonyIntents { */ public static final String ACTION_NETWORK_SET_TIMEZONE = "android.intent.action.NETWORK_SET_TIMEZONE"; + + /** + *

    Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms + *

    . + */ + // TODO(Moto): What is the use case, who is interested in this? + public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS + = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS"; + + /** + * Broadcast Action: The MDN changed during the CDMA OTA Process + * The intent will have the following extra values:

    + *
      + *
    • mdn - An Integer of the updated MDN number.
    • + *
    + * + *

    + */ + // TODO(Moto): Generally broadcast intents are for use to allow entities which + // may not know about each other to "communicate". This seems quite specific + // and maybe using the registrant style would be better. + public static final String ACTION_CDMA_OTA_MDN_CHANGED + = "android.intent.action.ACTION_MDN_STATE_CHANGED"; + } diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 453185f89bd97..4e8950fe1effa 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -98,4 +98,7 @@ public interface TelephonyProperties */ static String PROPERTY_DATA_NETWORK_TYPE = "gsm.network.type"; + /** Indicate if phone is in emergency callback mode */ + static final String PROPERTY_INECM_MODE = "ril.cdma.inecmmode"; + } diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 03f7f986da057..c0bfe5e6fd343 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -35,6 +35,7 @@ import android.text.TextUtils; import android.util.Log; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_INECM_MODE; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CommandsInterface; @@ -54,9 +55,9 @@ import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; -import java.util.ArrayList; import java.util.List; - +import java.util.Timer; +import java.util.TimerTask; /** * {@hide} */ @@ -83,8 +84,21 @@ public class CDMAPhone extends PhoneBase { // mEriFileLoadedRegistrants are informed after the ERI text has been loaded private RegistrantList mEriFileLoadedRegistrants = new RegistrantList(); + + // mECMExitRespRegistrant is informed after the phone has been exited + //the emergency callback mode + //keep track of if phone is in emergency callback mode + private boolean mIsPhoneInECMState; + private Registrant mECMExitRespRegistrant; private String mEsn; private String mMeid; + + // A runnable which is used to automatically exit from ECM after a period of time. + private Runnable mExitEcmRunnable = new Runnable() { + public void run() { + exitEmergencyCallbackMode(); + } + }; Registrant mPostDialHandler; @@ -122,13 +136,16 @@ public class CDMAPhone extends PhoneBase { mCM.setOnCallRing(h, EVENT_CALL_RING, null); mSST.registerForNetworkAttach(h, EVENT_REGISTERED_TO_NETWORK, null); mCM.registerForNVReady(h, EVENT_NV_READY, null); - mCM.registerForCdmaCallWaiting(h,EVENT_CDMA_CALL_WAITING,null); - mCM.setEmergencyCallbackMode(h, EVENT_EMERGENCY_CALLBACK_MODE, null); + mCM.setEmergencyCallbackMode(h, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null); //Change the system setting SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, new Integer(RILConstants.CDMA_PHONE).toString()); + + // TODO(Moto): Is this needed to handle phone crashes and/or power cycling? + String inEcm=SystemProperties.get(PROPERTY_INECM_MODE, "false"); + mIsPhoneInECMState = inEcm.equals("true"); } public void dispose() { @@ -143,7 +160,7 @@ public class CDMAPhone extends PhoneBase { mSST.unregisterForNetworkAttach(h); //EVENT_REGISTERED_TO_NETWORK mCM.unSetOnSuppServiceNotification(h); mCM.unSetOnCallRing(h); - mCM.unregisterForCdmaCallWaiting(h); + //Force all referenced classes to unregister their former registered events mCT.dispose(); @@ -370,8 +387,8 @@ public class CDMAPhone extends PhoneBase { return mRuimRecords.getMdnNumber(); } - public String getMin() { - return mRuimRecords.getMin(); + public String getCdmaMIN() { + return mRuimRecords.getCdmaMin(); } public void getCallWaiting(Message onComplete) { @@ -434,7 +451,7 @@ public class CDMAPhone extends PhoneBase { } public void setOnPostDialCharacter(Handler h, int what, Object obj) { - Log.e(LOG_TAG, "setOnPostDialCharacter: not possible in CDMA"); + mPostDialHandler = new Registrant(h, what, obj); } public boolean handlePinMmi(String dialString) { @@ -478,6 +495,30 @@ public class CDMAPhone extends PhoneBase { mDataConnection.setDataOnRoamingEnabled(enable); } + public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj) { + mCM.registerForCdmaOtaProvision(h, what, obj); + } + + public void unregisterForCdmaOtaStatusChange(Handler h) { + mCM.unregisterForCdmaOtaProvision(h); + } + + public void setOnEcbModeExitResponse(Handler h, int what, Object obj) { + mECMExitRespRegistrant = new Registrant (h, what, obj); + } + + public void unsetOnEcbModeExitResponse(Handler h) { + mECMExitRespRegistrant.clear(); + } + + public void registerForCallWaiting(Handler h, int what, Object obj) { + Log.e(LOG_TAG, "method registerForCallWaiting is NOT yet supported in CDMA"); + } + + public void unregisterForCallWaiting(Handler h) { + Log.e(LOG_TAG, "method unregisterForCallWaiting is NOT yet supported in CDMA"); + } + public String getIpAddress(String apnType) { return mDataConnection.getIpAddress(); } @@ -565,7 +606,7 @@ public class CDMAPhone extends PhoneBase { mCM.stopDtmf(null); } - public void sendBurstDtmf(String dtmfString) { + public void sendBurstDtmf(String dtmfString, Message onComplete) { boolean check = true; for (int itr = 0;itr < dtmfString.length(); itr++) { if (!PhoneNumberUtils.is12Key(dtmfString.charAt(itr))) { @@ -576,7 +617,7 @@ public class CDMAPhone extends PhoneBase { } } if ((mCT.state == Phone.State.OFFHOOK)&&(check)) { - mCM.sendBurstDtmf(dtmfString, null); + mCM.sendBurstDtmf(dtmfString, onComplete); } } @@ -593,7 +634,7 @@ public class CDMAPhone extends PhoneBase { } public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { - Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); + Log.e(LOG_TAG, "setOutgoingCallerIdDisplay: not possible in CDMA"); } public void enableLocationUpdates() { @@ -630,7 +671,14 @@ public class CDMAPhone extends PhoneBase { //TODO: Where can we get this value has to be clarified with QC //return mSIMRecords.getVoiceMailNumber(); // throw new RuntimeException(); - return "12345"; + return "*86"; + } + + /* Returns Number of Voicemails + * @hide + */ + public int getCountVoiceMessages() { + return mRuimRecords.getCountVoiceMessages(); } public String getVoiceMailAlphaTag() { @@ -648,7 +696,15 @@ public class CDMAPhone extends PhoneBase { } public boolean enableDataConnectivity() { - return mDataConnection.setDataEnabled(true); + + // block data activities when phone is in emergency callback mode + if (mIsPhoneInECMState) { + Intent intent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS); + ActivityManagerNative.broadcastStickyIntent(intent, null); + return false; + } else { + return mDataConnection.setDataEnabled(true); + } } public void disableLocationUpdates() { @@ -691,7 +747,7 @@ public class CDMAPhone extends PhoneBase { return null; } - /** + /** * Notify any interested party of a Phone state change. */ /*package*/ void notifyPhoneStateChanged() { @@ -736,6 +792,13 @@ public class CDMAPhone extends PhoneBase { mUnknownConnectionRegistrants.notifyResult(this); } + void sendEmergencyCallbackModeChange(){ + //Send an Intent + Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); + intent.putExtra(PHONE_IN_ECM_STATE, mIsPhoneInECMState); + ActivityManagerNative.broadcastStickyIntent(intent,null); + } + /*package*/ void updateMessageWaitingIndicator(boolean mwi) { // this also calls notifyMessageWaitingIndicator() @@ -761,6 +824,51 @@ public class CDMAPhone extends PhoneBase { mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, fc, null)); } + + @Override + public void exitEmergencyCallbackMode() { + // Send a message which will invoke handleExitEmergencyCallbackMode + mCM.exitEmergencyCallbackMode(h.obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE)); + } + + private void handleEnterEmergencyCallbackMode(Message msg) { + Log.d(LOG_TAG, "Event EVENT_EMERGENCY_CALLBACK_MODE Received"); + // if phone is not in ECM mode, and it's changed to ECM mode + if (mIsPhoneInECMState == false) { + mIsPhoneInECMState = true; + // notify change + sendEmergencyCallbackModeChange(); + setSystemProperty(PROPERTY_INECM_MODE, "true"); + + // Post this runnable so we will automatically exit + // if no one invokes exitEmergencyCallbackMode() directly. + // TODO(Moto): Get the delay a property so it can be adjusted + long delayInMillis = 300000; // 30,000 millis == 5 minutes + h.postDelayed(mExitEcmRunnable, delayInMillis); + } + } + + private void handleExitEmergencyCallbackMode(Message msg) { + Log.d(LOG_TAG, "Event EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE Received"); + AsyncResult ar = (AsyncResult)msg.obj; + + // Remove pending exit ECM runnable, if any + h.removeCallbacks(mExitEcmRunnable); + + if (mECMExitRespRegistrant != null) { + mECMExitRespRegistrant.notifyRegistrant(ar); + } + // if exiting ecm success + if (ar.exception == null) { + if (mIsPhoneInECMState) { + mIsPhoneInECMState = false; + setSystemProperty(PROPERTY_INECM_MODE, "false"); + } + // send an Intent + sendEmergencyCallbackModeChange(); + } + } + //***** Inner Classes class MyHandler extends Handler { MyHandler() { @@ -770,6 +878,7 @@ public class CDMAPhone extends PhoneBase { super(l); } + @Override public void handleMessage(Message msg) { AsyncResult ar; Message onComplete; @@ -806,12 +915,16 @@ public class CDMAPhone extends PhoneBase { } break; - case EVENT_EMERGENCY_CALLBACK_MODE: { - Log.d(LOG_TAG, "Event EVENT_EMERGENCY_CALLBACK_MODE Received"); - Intent intent = - new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_ENTERED); - ActivityManagerNative.broadcastStickyIntent(intent, null); + case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{ + handleEnterEmergencyCallbackMode(msg); } + break; + + case EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{ + handleExitEmergencyCallbackMode(msg); + } + break; + case EVENT_RUIM_RECORDS_LOADED:{ Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received"); } @@ -852,11 +965,7 @@ public class CDMAPhone extends PhoneBase { Log.d(LOG_TAG, "ERI read, notify registrants"); mEriFileLoadedRegistrants.notifyRegistrants(); } - } - break; - - case EVENT_CDMA_CALL_WAITING:{ - Log.d(LOG_TAG, "Event EVENT_CDMA_CALL_WAITING Received"); + setSystemProperty(PROPERTY_INECM_MODE,"false"); } break; @@ -867,26 +976,26 @@ public class CDMAPhone extends PhoneBase { } } - /** - * Retrieves the PhoneSubInfo of the CDMAPhone - */ - public PhoneSubInfo getPhoneSubInfo(){ + /** + * Retrieves the PhoneSubInfo of the CDMAPhone + */ + public PhoneSubInfo getPhoneSubInfo() { return mSubInfo; - } + } - /** - * Retrieves the IccSmsInterfaceManager of the CDMAPhone - */ - public IccSmsInterfaceManager getIccSmsInterfaceManager(){ - return mRuimSmsInterfaceManager; - } + /** + * Retrieves the IccSmsInterfaceManager of the CDMAPhone + */ + public IccSmsInterfaceManager getIccSmsInterfaceManager() { + return mRuimSmsInterfaceManager; + } - /** - * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone - */ - public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){ - return mRuimPhoneBookInterfaceManager; - } + /** + * Retrieves the IccPhoneBookInterfaceManager of the CDMAPhone + */ + public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager() { + return mRuimPhoneBookInterfaceManager; + } public void registerForNvLoaded(Handler h, int what, Object obj) { Registrant r = new Registrant (h, what, obj); @@ -906,97 +1015,146 @@ public class CDMAPhone extends PhoneBase { mEriFileLoadedRegistrants.remove(h); } - // override for allowing access from other classes of this package - /** - * {@inheritDoc} - */ - public final void setSystemProperty(String property, String value) { - super.setSystemProperty(property, value); - } - - /** - * {@inheritDoc} - */ - public Handler getHandler(){ - return h; - } - - /** - * {@inheritDoc} - */ - public IccFileHandler getIccFileHandler(){ - return this.mIccFileHandler; - } - - /** - * Set the TTY mode of the CDMAPhone - */ - public void setTTYMode(int ttyMode, Message onComplete) { - this.mCM.setTTYMode(ttyMode, onComplete); -} - - /** - * Queries the TTY mode of the CDMAPhone - */ - public void queryTTYMode(Message onComplete) { - this.mCM.queryTTYMode(onComplete); - } - - /** - * Sends Exit EmergencyCallbackMode Exit request on CDMAPhone - */ - public void exitEmergencyCallbackMode(Message onComplete) { - this.mCM.exitEmergencyCallbackMode(onComplete); - } - - /** - * Activate or deactivate cell broadcast SMS. - * - * @param activate - * 0 = activate, 1 = deactivate - * @param response - * Callback message is empty on completion - */ - public void activateCellBroadcastSms(int activate, Message response) { - mSMS.activateCellBroadcastSms(activate, response); - } - - /** - * Query the current configuration of cdma cell broadcast SMS. - * - * @param response - * Callback message is empty on completion - */ - public void getCellBroadcastSmsConfig(Message response){ - mSMS.getCellBroadcastSmsConfig(response); - } - - /** - * Configure cdma cell broadcast SMS. - * - * @param response - * Callback message is empty on completion - */ - public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){ - mSMS.setCellBroadcastConfig(configValuesArray, response); - } - - public void registerForOtaSessionStatus(Handler h, int what, Object obj){ - mCM.registerForOtaSessionStatus(h, what, obj); - } - - public void unregisterForOtaSessionStatus(Handler h){ - mCM.unregisterForOtaSessionStatus(h); - } - -/** - * TODO(Teleca): The code in getCdmaEriIconIndex, getCdmaEriIconMode & getCdmaEriText share a - * lot of logic, refactor. - */ + // override for allowing access from other classes of this package /** - * Returns the CDMA ERI icon index to display, - * it returns 1, EriInfo.ROAMING_INDICATOR_OFF, in case there is no icon to display + * {@inheritDoc} */ + public final void setSystemProperty(String property, String value) { + super.setSystemProperty(property, value); + } + + /** + * {@inheritDoc} + */ + public Handler getHandler() { + return h; + } + + /** + * {@inheritDoc} + */ + public IccFileHandler getIccFileHandler() { + return this.mIccFileHandler; + } + + /** + * Set the TTY mode of the CDMAPhone + */ + public void setTTYMode(int ttyMode, Message onComplete) { + this.mCM.setTTYMode(ttyMode, onComplete); + } + + /** + * Queries the TTY mode of the CDMAPhone + */ + public void queryTTYMode(Message onComplete) { + this.mCM.queryTTYMode(onComplete); + } + + /** + * Activate or deactivate cell broadcast SMS. + * + * @param activate 0 = activate, 1 = deactivate + * @param response Callback message is empty on completion + */ + public void activateCellBroadcastSms(int activate, Message response) { + mSMS.activateCellBroadcastSms(activate, response); + } + + /** + * Query the current configuration of cdma cell broadcast SMS. + * + * @param response Callback message is empty on completion + */ + public void getCellBroadcastSmsConfig(Message response) { + mSMS.getCellBroadcastSmsConfig(response); + } + + /** + * Configure cdma cell broadcast SMS. + * + * @param response Callback message is empty on completion + */ + public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) { + mSMS.setCellBroadcastConfig(configValuesArray, response); + } + + public static final String IS683A_FEATURE_CODE = "*228" ; + public static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4 ; + public static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2 ; + public static final int IS683A_SYS_SEL_CODE_OFFSET = 4; + + private static final int IS683_CONST_800MHZ_A_BAND = 0; + private static final int IS683_CONST_800MHZ_B_BAND = 1; + private static final int IS683_CONST_1900MHZ_A_BLOCK = 2; + private static final int IS683_CONST_1900MHZ_B_BLOCK = 3; + private static final int IS683_CONST_1900MHZ_C_BLOCK = 4; + private static final int IS683_CONST_1900MHZ_D_BLOCK = 5; + private static final int IS683_CONST_1900MHZ_E_BLOCK = 6; + private static final int IS683_CONST_1900MHZ_F_BLOCK = 7; + + private boolean isIs683OtaSpDialStr(String dialStr) { + int sysSelCodeInt; + boolean isOtaspDialString = false; + int dialStrLen = dialStr.length(); + + if (dialStrLen == IS683A_FEATURE_CODE_NUM_DIGITS) { + if (dialStr.equals(IS683A_FEATURE_CODE)) { + isOtaspDialString = true; + } + } else if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE, 0, + IS683A_FEATURE_CODE_NUM_DIGITS) == true) + && (dialStrLen >= + (IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS))) { + StringBuilder sb = new StringBuilder(dialStr); + // Separate the System Selection Code into its own string + char[] sysSel = new char[2]; + sb.delete(0, IS683A_SYS_SEL_CODE_OFFSET); + sb.getChars(0, IS683A_SYS_SEL_CODE_NUM_DIGITS, sysSel, 0); + + if ((PhoneNumberUtils.isISODigit(sysSel[0])) + && (PhoneNumberUtils.isISODigit(sysSel[1]))) { + String sysSelCode = new String(sysSel); + sysSelCodeInt = Integer.parseInt((String)sysSelCode); + switch (sysSelCodeInt) { + case IS683_CONST_800MHZ_A_BAND: + case IS683_CONST_800MHZ_B_BAND: + case IS683_CONST_1900MHZ_A_BLOCK: + case IS683_CONST_1900MHZ_B_BLOCK: + case IS683_CONST_1900MHZ_C_BLOCK: + case IS683_CONST_1900MHZ_D_BLOCK: + case IS683_CONST_1900MHZ_E_BLOCK: + case IS683_CONST_1900MHZ_F_BLOCK: + isOtaspDialString = true; + break; + + default: + break; + } + } + } + return isOtaspDialString; + } + + /** + * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier + * OTASP dial string. + * + * @param dialStr the number to look up. + * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string + */ + @Override + public boolean isOtaSpNumber(String dialStr){ + boolean isOtaSpNum = false; + if(dialStr != null){ + isOtaSpNum=isIs683OtaSpDialStr(dialStr); + if(isOtaSpNum == false){ + //TO DO:Add carrier specific OTASP number detection here. + } + } + return isOtaSpNum; + } + @Override public int getCdmaEriIconIndex() { int roamInd = getServiceState().getCdmaRoamingIndicator(); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java index a1d362fb600f5..c02fcd4a3a8e2 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java @@ -24,6 +24,7 @@ import android.os.RegistrantList; import android.telephony.PhoneNumberUtils; import android.telephony.ServiceState; import android.util.Log; +import android.os.SystemProperties; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CallTracker; @@ -31,11 +32,12 @@ import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; import com.android.internal.telephony.DriverCall; import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; import java.util.List; + /** * {@hide} */ @@ -69,11 +71,12 @@ public final class CdmaCallTracker extends CallTracker { CdmaConnection pendingMO; boolean hangupPendingMO; - + boolean pendingCallInECM=false; CDMAPhone phone; boolean desiredMute = false; // false = mute off + int pendingCallClirMode; Phone.State state = Phone.State.IDLE; @@ -115,6 +118,7 @@ public final class CdmaCallTracker extends CallTracker { } + @Override protected void finalize() { Log.d(LOG_TAG, "CdmaCallTracker finalized"); } @@ -204,7 +208,15 @@ public final class CdmaCallTracker extends CallTracker { // Always unmute when initiating a new call setMute(false); - cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); + if(inEcm.equals("false")) { + cm.dial(pendingMO.address, clirMode, obtainCompleteMessage()); + } else { + phone.exitEmergencyCallbackMode(); + phone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); + pendingCallClirMode=clirMode; + pendingCallInECM=true; + } } updatePhoneState(); @@ -536,6 +548,9 @@ public final class CdmaCallTracker extends CallTracker { droppedDuringPoll.add(pendingMO); pendingMO = null; hangupPendingMO = false; + if( pendingCallInECM) { + pendingCallInECM = false; + } } if (newRinging != null) { @@ -847,8 +862,17 @@ public final class CdmaCallTracker extends CallTracker { handleRadioNotAvailable(); break; + case EVENT_EXIT_ECM_RESPONSE_CDMA: + //no matter the result, we still do the same here + if (pendingCallInECM) { + cm.dial(pendingMO.address, pendingCallClirMode, obtainCompleteMessage()); + pendingCallInECM = false; + } + phone.unsetOnEcbModeExitResponse(this); + break; + default:{ - throw new RuntimeException("unexpected event not handled"); + throw new RuntimeException("unexpected event not handled"); } } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaiting.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaiting.java deleted file mode 100644 index 64841d7ed0328..0000000000000 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaiting.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.telephony.cdma; - -import com.android.internal.telephony.CdmaInformationRecord; - -public class CdmaCallWaiting { - public String number; - public int numberPresentation; - public String name; - - public CdmaInformationRecord.CdmaSignalInfoRec signalInfoRecord = - new CdmaInformationRecord.CdmaSignalInfoRec(); - - @Override - public String toString() { - return "CdmaCallWaiting: {" + " number: " + number + " numberPresentation: " - + numberPresentation + " name: " + name + " signalInfoRecord: " - + signalInfoRecord + " }"; - } -} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java new file mode 100644 index 0000000000000..54dec48e84354 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +/** + * Represents a Supplementary Service Notification received from the network. + * + * {@hide} + */ +public class CdmaCallWaitingNotification { + public String number =null; + public int numberPresentation = 0; + public String name = null; + public int namePresentation = 0; + public int isPresent = 0; + public int signalType = 0; + public int alertPitch = 0; + public int signal = 0; + + + public String toString() + { + return super.toString() + "Call Waiting Notification " + + " number: " + number + + " numberPresentation: " + numberPresentation + + " name: " + name + + " namePresentation: " + namePresentation + + " isPresent: " + isPresent + + " signalType: " + signalType + + " alertPitch: " + alertPitch + + " signal: " + signal ; + } + +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java index 0a237c6fb942c..32442f60a0b30 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java @@ -48,7 +48,7 @@ public class CdmaConnection extends Connection { String postDialString; // outgoing calls only boolean isIncoming; boolean disconnected; - + String cnapName; int index; // index in CdmaCallTracker.connections[], -1 if unassigned /* @@ -74,6 +74,8 @@ public class CdmaConnection extends Connection { DisconnectCause cause = DisconnectCause.NOT_DISCONNECTED; PostDialState postDialState = PostDialState.NOT_STARTED; int numberPresentation = Connection.PRESENTATION_ALLOWED; + int cnapNamePresentation = Connection.PRESENTATION_ALLOWED; + Handler h; @@ -86,10 +88,19 @@ public class CdmaConnection extends Connection { static final int EVENT_WAKE_LOCK_TIMEOUT = 4; //***** Constants - static final int PAUSE_DELAY_FIRST_MILLIS = 100; - static final int PAUSE_DELAY_MILLIS = 3 * 1000; static final int WAKE_LOCK_TIMEOUT_MILLIS = 60*1000; - + static final int PAUSE_DELAY_MILLIS = 2 * 1000; + + // TODO(Moto): These should be come from a resourse file + // at a minimum as different carriers may want to use + // different characters and our general default is "," & ";". + // Furthermore Android supports contacts that have phone + // numbers entered as strings so '1-800-164flowers' would not + // be handled as expected. Both issues need to be resolved. + static final char CUSTOMERIZED_WAIT_CHAR_UPPER ='W'; + static final char CUSTOMERIZED_WAIT_CHAR_LOWER ='w'; + static final char CUSTOMERIZED_PAUSE_CHAR_UPPER ='P'; + static final char CUSTOMERIZED_PAUSE_CHAR_LOWER ='p'; //***** Inner Classes class MyHandler extends Handler { @@ -126,6 +137,8 @@ public class CdmaConnection extends Connection { isIncoming = dc.isMT; createTime = System.currentTimeMillis(); + cnapName = dc.name; + cnapNamePresentation = dc.namePresentation; numberPresentation = dc.numberPresentation; this.index = index; @@ -134,6 +147,16 @@ public class CdmaConnection extends Connection { parent.attach(this, dc); } + CdmaConnection () { + owner = null; + h = null; + address = null; + index = -1; + parent = null; + isIncoming = true; + createTime = System.currentTimeMillis(); + } + /** This is an MO call, created when dialing */ /*package*/ CdmaConnection (Context context, String dialString, CdmaCallTracker ct, CdmaCall parent) { @@ -144,6 +167,9 @@ public class CdmaConnection extends Connection { h = new MyHandler(owner.getLooper()); this.dialString = dialString; + Log.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString); + dialString = formatDialString(dialString); + Log.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString); this.address = PhoneNumberUtils.extractNetworkPortion(dialString); this.postDialString = PhoneNumberUtils.extractPostDialPortion(dialString); @@ -151,10 +177,15 @@ public class CdmaConnection extends Connection { index = -1; isIncoming = false; + cnapName = null; + cnapNamePresentation = 0; + numberPresentation = 0; createTime = System.currentTimeMillis(); - this.parent = parent; - parent.attachFake(this, CdmaCall.State.DIALING); + if (parent != null) { + this.parent = parent; + parent.attachFake(this, CdmaCall.State.DIALING); + } } public void dispose() { @@ -186,10 +217,22 @@ public class CdmaConnection extends Connection { return (isIncoming ? "incoming" : "outgoing"); } + public String getOrigDialString(){ + return dialString; + } + public String getAddress() { return address; } + public String getCnapName() { + return cnapName; + } + + public int getCnapNamePresentation() { + return cnapNamePresentation; + } + public CdmaCall getCall() { return parent; } @@ -320,6 +363,16 @@ public class CdmaConnection extends Connection { } } + /** + * Used for 3way call only + */ + void update (CdmaConnection c) { + address = c.address; + cnapName = c.cnapName; + cnapNamePresentation = c.cnapNamePresentation; + numberPresentation = c.numberPresentation; + } + public void cancelPostDial() { setPostDialState(PostDialState.CANCELLED); } @@ -355,7 +408,7 @@ public class CdmaConnection extends Connection { case CallFailCause.CDMA_LOCKED_UNTIL_POWER_CYCLE: return DisconnectCause.CDMA_LOCKED_UNTIL_POWER_CYCLE; case CallFailCause.CDMA_DROP: - return DisconnectCause.CDMA_DROP; + return DisconnectCause.LOST_SIGNAL; // TODO(Moto): wink/dave changed from CDMA_DROP; case CallFailCause.CDMA_INTERCEPT: return DisconnectCause.CDMA_INTERCEPT; case CallFailCause.CDMA_REORDER: @@ -434,6 +487,20 @@ public class CdmaConnection extends Connection { changed = true; } + // A null cnapName should be the same as "" + if (null != dc.name) { + if (cnapName != dc.name) { + cnapName = dc.name; + changed = true; + } + } else { + cnapName = ""; + // TODO(Moto): Should changed = true if cnapName wasn't previously "" + } + log("--dssds----"+cnapName); + cnapNamePresentation = dc.namePresentation; + numberPresentation = dc.numberPresentation; + if (newParent != parent) { if (parent != null) { parent.detach(this); @@ -533,25 +600,13 @@ public class CdmaConnection extends Connection { if (PhoneNumberUtils.is12Key(c)) { owner.cm.sendDtmf(c, h.obtainMessage(EVENT_DTMF_DONE)); } else if (c == PhoneNumberUtils.PAUSE) { - // From TS 22.101: + setPostDialState(PostDialState.PAUSE); - // "The first occurrence of the "DTMF Control Digits Separator" - // shall be used by the ME to distinguish between the addressing - // digits (i.e. the phone number) and the DTMF digits...." - - if (nextPostDialChar == 1) { - // The first occurrence. - // We don't need to pause here, but wait for just a bit anyway - h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), - PAUSE_DELAY_FIRST_MILLIS); - } else { - // It continues... - // "Upon subsequent occurrences of the separator, the UE shall - // pause again for 3 seconds (\u00B1 20 %) before sending any - // further DTMF digits." - h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), + // Upon occurrences of the separator, the UE shall + // pause again for 2 seconds before sending any + // further DTMF digits. + h.sendMessageDelayed(h.obtainMessage(EVENT_PAUSE_DONE), PAUSE_DELAY_MILLIS); - } } else if (c == PhoneNumberUtils.WAIT) { setPostDialState(PostDialState.WAIT); } else if (c == PhoneNumberUtils.WILD) { @@ -563,17 +618,40 @@ public class CdmaConnection extends Connection { return true; } - public String - getRemainingPostDialString() { + public String getRemainingPostDialString() { if (postDialState == PostDialState.CANCELLED - || postDialState == PostDialState.COMPLETE - || postDialString == null - || postDialString.length() <= nextPostDialChar - ) { + || postDialState == PostDialState.COMPLETE + || postDialString == null + || postDialString.length() <= nextPostDialChar) { return ""; } - return postDialString.substring(nextPostDialChar); + String subStr = postDialString.substring(nextPostDialChar); + if (subStr != null) { + int wIndex = subStr.indexOf(PhoneNumberUtils.WAIT); + int pIndex = subStr.indexOf(PhoneNumberUtils.PAUSE); + + // TODO(Moto): Courtesy of jsh; is this simpler expression equivalent? + // + // if (wIndex > 0 && (wIndex < pIndex || pIndex <= 0)) { + // subStr = subStr.substring(0, wIndex); + // } else if (pIndex > 0) { + // subStr = subStr.substring(0, pIndex); + // } + + if (wIndex > 0 && pIndex > 0) { + if (wIndex > pIndex) { + subStr = subStr.substring(0, pIndex); + } else { + subStr = subStr.substring(0, wIndex); + } + } else if (wIndex > 0) { + subStr = subStr.substring(0, wIndex); + } else if (pIndex > 0) { + subStr = subStr.substring(0, pIndex); + } + } + return subStr; } @Override @@ -591,8 +669,7 @@ public class CdmaConnection extends Connection { releaseWakeLock(); } - private void - processNextPostDialChar() { + void processNextPostDialChar() { char c = 0; Registrant postDialHandler; @@ -698,21 +775,18 @@ public class CdmaConnection extends Connection { postDialState = s; } - private void - createWakeLock(Context context) { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + private void createWakeLock(Context context) { + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG); } - private void - acquireWakeLock() { + private void acquireWakeLock() { log("acquireWakeLock"); mPartialWakeLock.acquire(); } - private void - releaseWakeLock() { - synchronized(mPartialWakeLock) { + private void releaseWakeLock() { + synchronized (mPartialWakeLock) { if (mPartialWakeLock.isHeld()) { log("releaseWakeLock"); mPartialWakeLock.release(); @@ -720,6 +794,119 @@ public class CdmaConnection extends Connection { } } + private static boolean isPause(char c) { + if (c == CUSTOMERIZED_PAUSE_CHAR_UPPER || c == CUSTOMERIZED_PAUSE_CHAR_LOWER + || c == PhoneNumberUtils.PAUSE) { + return true; + } else { + return false; + } + } + + private static boolean isWait(char c) { + if (c == CUSTOMERIZED_WAIT_CHAR_LOWER || c == CUSTOMERIZED_WAIT_CHAR_UPPER + || c == PhoneNumberUtils.WAIT) { + return true; + } else { + return false; + } + } + + /** + * format string + * convert "+" to "011" + * handle corner cases for PAUSE/WAIT + * If PAUSE/WAIT sequence at the end,ignore them + * If PAUSE/WAIT sequence in the middle, then if there is any WAIT + * in PAUSE/WAIT sequence, treat them like WAIT + * If PAUSE followed by WAIT or WAIT followed by PAUSE in the middle, + * treat them like just PAUSE or WAIT + */ + private static String formatDialString(String phoneNumber) { + if (phoneNumber == null) { + return null; + } + int length = phoneNumber.length(); + StringBuilder ret = new StringBuilder(); + + // TODO(Moto): Modifying the for loop index is confusing, a + // while loop is probably better and overall this code is + // hard to follow. If this was routine was refactored and + // used several private methods with good names to make it + // easier to follow. + for (int i = 0; i < length; i++) { + char c = phoneNumber.charAt(i); + + if (PhoneNumberUtils.isDialable(c)) { + if (c == '+') { + // TODO(Moto): Is this valid for "all" countries???? + // should probably be pulled from a resource based + // on current contry code (MCC). + ret.append("011"); + } else { + ret.append(c); + } + } else if (isPause(c) || isWait(c)) { + if (i < length - 1) { // if PAUSE/WAIT not at the end + int index = 0; + boolean wMatched = false; + for (index = i + 1; index < length; index++) { + char cNext = phoneNumber.charAt(index); + // if there is any W inside P/W sequence,mark it + if (isWait(cNext)) { + wMatched = true; + } + // if any characters other than P/W chars after P/W sequence + // we break out the loop and append the correct + if (!isWait(cNext) && !isPause(cNext)) { + break; + } + } + if (index == length) { + // it means there is no dialable character after PAUSE/WAIT + i = length - 1; + break; + } else {// means index "; + } + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index dbb0f93037395..8ecdecd4d9c77 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -24,6 +24,7 @@ import android.database.ContentObserver; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; +import android.os.PowerManager; import android.os.Registrant; import android.os.RegistrantList; import android.os.SystemClock; @@ -38,7 +39,9 @@ import android.telephony.cdma.CdmaCellLocation; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import android.util.Config; import android.util.TimeUtils; +import java.util.Calendar; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; @@ -100,12 +103,26 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { private RegistrantList cdmaDataConnectionAttachedRegistrants = new RegistrantList(); private RegistrantList cdmaDataConnectionDetachedRegistrants = new RegistrantList(); + // Sometimes we get the NITZ time before we know what country we are in. + // Keep the time zone information from the NITZ string so we can fix + // the time zone once know the country. + private boolean mNeedFixZone = false; + private int mZoneOffset; + private boolean mZoneDst; + private long mZoneTime; private boolean mGotCountryCode = false; + String mSavedTimeZone; + long mSavedTime; + long mSavedAtTime; // We can't register for SIM_RECORDS_LOADED immediately because the // SIMRecords object may not be instantiated yet. private boolean mNeedToRegForRuimLoaded; + // Wake lock used while setting time of day. + private PowerManager.WakeLock mWakeLock; + private static final String WAKELOCK_TAG = "ServiceStateTracker"; + // Keep track of SPN display rules, so we only broadcast intent if something changes. private String curSpn = null; private String curEriText = null; @@ -134,7 +151,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { @Override public void onChange(boolean selfChange) { Log.i("CdmaServiceStateTracker", "Auto time state called ..."); - //NOTE in CDMA NITZ is not used + revertToNitz(); + } }; @@ -152,10 +170,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { newCellLoc = new CdmaCellLocation(); mSignalStrength = new SignalStrength(); + PowerManager powerManager = + (PowerManager)phone.getContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); + cm.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); cm.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null); cm.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED_CDMA, null); + cm.setOnNITZTime(this, EVENT_NITZ_TIME, null); cm.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null); cm.registerForRUIMReady(this, EVENT_RUIM_READY, null); @@ -188,6 +211,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { phone.unregisterForEriFileLoaded(this); phone.mRuimRecords.unregisterForRecordsLoaded(this); cm.unSetOnSignalStrengthUpdate(this); + cm.unSetOnNITZTime(this); cr.unregisterContentObserver(this.mAutoTimeObserver); } @@ -378,6 +402,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { cm.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH)); break; + case EVENT_NITZ_TIME: + ar = (AsyncResult) msg.obj; + + String nitzString = (String)((Object[])ar.result)[0]; + long nitzReceiveTime = ((Long)((Object[])ar.result)[1]).longValue(); + + setTimeFromNITZString(nitzString, nitzReceiveTime); + break; + case EVENT_SIGNAL_STRENGTH_UPDATE: // This is a notification from // CommandsInterface.setOnSignalStrengthUpdate @@ -800,8 +833,44 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return ret; } - private void - pollStateDone() { + private void fixTimeZone(String isoCountryCode) { + TimeZone zone = null; + // If the offset is (0, false) and the time zone property + // is set, use the time zone property rather than GMT. + String zoneName = SystemProperties.get(TIMEZONE_PROPERTY); + if ((mZoneOffset == 0) && (mZoneDst == false) && (zoneName != null) + && (zoneName.length() > 0) + && (Arrays.binarySearch(GMT_COUNTRY_CODES, isoCountryCode) < 0)) { + // For NITZ string without time zone, + // need adjust time to reflect default time zone setting + zone = TimeZone.getDefault(); + long tzOffset; + tzOffset = zone.getOffset(System.currentTimeMillis()); + if (getAutoTime()) { + setAndBroadcastNetworkSetTime(System.currentTimeMillis() - tzOffset); + } else { + // Adjust the saved NITZ time to account for tzOffset. + mSavedTime = mSavedTime - tzOffset; + } + } else if (isoCountryCode.equals("")) { + // Country code not found. This is likely a test network. + // Get a TimeZone based only on the NITZ parameters (best guess). + zone = getNitzTimeZone(mZoneOffset, mZoneDst, mZoneTime); + } else { + zone = TimeUtils.getTimeZone(mZoneOffset, mZoneDst, mZoneTime, isoCountryCode); + } + + mNeedFixZone = false; + + if (zone != null) { + if (getAutoTime()) { + setAndBroadcastNetworkSetTimeZone(zone.getID()); + } + saveNitzTimeZone(zone.getID()); + } + } + + private void pollStateDone() { if (DBG) log("Poll ServiceState done: oldSS=[" + ss + "] newSS=[" + newSS + "]"); boolean hasRegistered = @@ -896,9 +965,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { if (operatorNumeric == null) { phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, ""); } else { - String iso = ""; + String isoCountryCode = ""; try{ - iso = MccTable.countryCodeForMcc(Integer.parseInt( + isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt( operatorNumeric.substring(0,3))); } catch ( NumberFormatException ex){ Log.w(LOG_TAG, "countryCodeForMcc error" + ex); @@ -906,8 +975,11 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { Log.w(LOG_TAG, "countryCodeForMcc error" + ex); } - phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso); + phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, isoCountryCode); mGotCountryCode = true; + if (mNeedFixZone) { + fixTimeZone(isoCountryCode); + } } phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING, @@ -1123,6 +1195,169 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { return cdmaRoaming && !(equalsOnsl || equalsOnss); } + + /** + * nitzReceiveTime is time_t that the NITZ time was posted + */ + + private + void setTimeFromNITZString (String nitz, long nitzReceiveTime) + { + // "yy/mm/dd,hh:mm:ss(+/-)tz" + // tz is in number of quarter-hours + + long start = SystemClock.elapsedRealtime(); + Log.i(LOG_TAG, "NITZ: " + nitz + "," + nitzReceiveTime + + " start=" + start + " delay=" + (start - nitzReceiveTime)); + + try { + /* NITZ time (hour:min:sec) will be in UTC but it supplies the timezone + * offset as well (which we won't worry about until later) */ + Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + + c.clear(); + c.set(Calendar.DST_OFFSET, 0); + + String[] nitzSubs = nitz.split("[/:,+-]"); + + int year = 2000 + Integer.parseInt(nitzSubs[0]); + c.set(Calendar.YEAR, year); + + // month is 0 based! + int month = Integer.parseInt(nitzSubs[1]) - 1; + c.set(Calendar.MONTH, month); + + int date = Integer.parseInt(nitzSubs[2]); + c.set(Calendar.DATE, date); + + int hour = Integer.parseInt(nitzSubs[3]); + c.set(Calendar.HOUR, hour); + + int minute = Integer.parseInt(nitzSubs[4]); + c.set(Calendar.MINUTE, minute); + + int second = Integer.parseInt(nitzSubs[5]); + c.set(Calendar.SECOND, second); + + boolean sign = (nitz.indexOf('-') == -1); + + int tzOffset = Integer.parseInt(nitzSubs[6]); + + int dst = (nitzSubs.length >= 8 ) ? Integer.parseInt(nitzSubs[7]) + : 0; + + // The zone offset received from NITZ is for current local time, + // so DST correction is already applied. Don't add it again. + // + // tzOffset += dst * 4; + // + // We could unapply it if we wanted the raw offset. + + tzOffset = (sign ? 1 : -1) * tzOffset * 15 * 60 * 1000; + + TimeZone zone = null; + + // As a special extension, the Android emulator appends the name of + // the host computer's timezone to the nitz string. this is zoneinfo + // timezone name of the form Area!Location or Area!Location!SubLocation + // so we need to convert the ! into / + if (nitzSubs.length >= 9) { + String tzname = nitzSubs[8].replace('!','/'); + zone = TimeZone.getTimeZone( tzname ); + } + + String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY); + + if (zone == null) { + + if (mGotCountryCode) { + if (iso != null && iso.length() > 0) { + zone = TimeUtils.getTimeZone(tzOffset, dst != 0, + c.getTimeInMillis(), + iso); + } else { + // We don't have a valid iso country code. This is + // most likely because we're on a test network that's + // using a bogus MCC (eg, "001"), so get a TimeZone + // based only on the NITZ parameters. + zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis()); + } + } + } + + if (zone == null) { + // We got the time before the country, so we don't know + // how to identify the DST rules yet. Save the information + // and hope to fix it up later. + + mNeedFixZone = true; + mZoneOffset = tzOffset; + mZoneDst = dst != 0; + mZoneTime = c.getTimeInMillis(); + } + + if (zone != null) { + if (getAutoTime()) { + setAndBroadcastNetworkSetTimeZone(zone.getID()); + } + saveNitzTimeZone(zone.getID()); + } + + String ignore = SystemProperties.get("gsm.ignore-nitz"); + if (ignore != null && ignore.equals("yes")) { + Log.i(LOG_TAG, "NITZ: Not setting clock because gsm.ignore-nitz is set"); + return; + } + + try { + mWakeLock.acquire(); + + if (getAutoTime()) { + long millisSinceNitzReceived + = SystemClock.elapsedRealtime() - nitzReceiveTime; + + if (millisSinceNitzReceived < 0) { + // Sanity check: something is wrong + Log.i(LOG_TAG, "NITZ: not setting time, clock has rolled " + + "backwards since NITZ time was received, " + + nitz); + return; + } + + if (millisSinceNitzReceived > Integer.MAX_VALUE) { + // If the time is this far off, something is wrong > 24 days! + Log.i(LOG_TAG, "NITZ: not setting time, processing has taken " + + (millisSinceNitzReceived / (1000 * 60 * 60 * 24)) + + " days"); + return; + } + + // Note: with range checks above, cast to int is safe + c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived); + + Log.i(LOG_TAG, "NITZ: Setting time of day to " + c.getTime() + + " NITZ receive delay(ms): " + millisSinceNitzReceived + + " gained(ms): " + + (c.getTimeInMillis() - System.currentTimeMillis()) + + " from " + nitz); + + setAndBroadcastNetworkSetTime(c.getTimeInMillis()); + Log.i(LOG_TAG, "NITZ: after Setting time of day"); + } + SystemProperties.set("gsm.nitz.time", String.valueOf(c.getTimeInMillis())); + saveNitzTime(c.getTimeInMillis()); + if (Config.LOGV) { + long end = SystemClock.elapsedRealtime(); + Log.v(LOG_TAG, "NITZ: end=" + end + " dur=" + (end - start)); + } + } finally { + mWakeLock.release(); + } + } catch (RuntimeException ex) { + Log.e(LOG_TAG, "NITZ: Parsing NITZ time " + nitz, ex); + } + } + private boolean getAutoTime() { try { return Settings.System.getInt(phone.getContext().getContentResolver(), @@ -1132,6 +1367,58 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } } + private void saveNitzTimeZone(String zoneId) { + mSavedTimeZone = zoneId; + } + + private void saveNitzTime(long time) { + mSavedTime = time; + mSavedAtTime = SystemClock.elapsedRealtime(); + } + + /** + * Set the timezone and send out a sticky broadcast so the system can + * determine if the timezone was set by the carrier. + * + * @param zoneId timezone set by carrier + */ + private void setAndBroadcastNetworkSetTimeZone(String zoneId) { + AlarmManager alarm = + (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE); + alarm.setTimeZone(zoneId); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE); + intent.putExtra("time-zone", zoneId); + phone.getContext().sendStickyBroadcast(intent); + } + + /** + * Set the time and Send out a sticky broadcast so the system can determine + * if the time was set by the carrier. + * + * @param time time set by network + */ + private void setAndBroadcastNetworkSetTime(long time) { + SystemClock.setCurrentTimeMillis(time); + Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME); + intent.putExtra("time", time); + phone.getContext().sendStickyBroadcast(intent); + } + + private void revertToNitz() { + if (Settings.System.getInt(phone.getContext().getContentResolver(), + Settings.System.AUTO_TIME, 0) == 0) { + return; + } + Log.d(LOG_TAG, "Reverting to NITZ: tz='" + mSavedTimeZone + + "' mSavedTime=" + mSavedTime + + " mSavedAtTime=" + mSavedAtTime); + if (mSavedTimeZone != null && mSavedTime != 0 && mSavedAtTime != 0) { + setAndBroadcastNetworkSetTimeZone(mSavedTimeZone); + setAndBroadcastNetworkSetTime(mSavedTime + + (SystemClock.elapsedRealtime() - mSavedAtTime)); + } + } + /** * @return true if phone is camping on a technology * that could support voice and data simultaneously. diff --git a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java index c226b62eef439..85fe6dc486583 100644 --- a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java +++ b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java @@ -84,7 +84,7 @@ public final class FeatureCode extends Handler implements MmiCode { CDMAPhone phone; Context context; - + CdmaConnection suppConn; String action; // '*' in CDMA String sc; // Service Code String poundString; // Entire Flash string @@ -239,8 +239,15 @@ public final class FeatureCode extends Handler implements MmiCode { /** Process a Flash Code...anything that isn't a dialing number */ void processCode () { Log.d(LOG_TAG, "send feature code..."); - phone.mCM.sendCDMAFeatureCode(this.poundString, - obtainMessage(EVENT_CDMA_FLASH_COMPLETED)); + if (this.poundString != null) { + // TODO(Moto): Is suppConn going away? + suppConn = new CdmaConnection(phone.getContext(), this.poundString, phone.mCT, null); + phone.mCM.sendCDMAFeatureCode(suppConn.address, + obtainMessage(EVENT_CDMA_FLASH_COMPLETED)); + } else { + phone.mCM.sendCDMAFeatureCode(this.poundString, + obtainMessage(EVENT_CDMA_FLASH_COMPLETED)); + } } /** Called from CDMAPhone.handleMessage; not a Handler subclass */ @@ -261,6 +268,7 @@ public final class FeatureCode extends Handler implements MmiCode { } else { state = State.COMPLETE; message = context.getText(com.android.internal.R.string.fcComplete); + suppConn.processNextPostDialChar(); } phone.onFeatureCodeDone(this); break; diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java index 321824fbce249..d5b83793a7771 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java @@ -39,6 +39,10 @@ import com.android.internal.telephony.IccRecords; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.TelephonyIntents; +import android.app.ActivityManagerNative; +import android.content.Intent; + /** * {@hide} @@ -47,6 +51,7 @@ public final class RuimRecords extends IccRecords { static final String LOG_TAG = "CDMA"; private static final boolean DBG = true; + private boolean m_ota_commited=false; //***** Instance Variables @@ -73,6 +78,7 @@ public final class RuimRecords extends IccRecords { private static final int EVENT_GET_SMS_DONE = 22; private static final int EVENT_RUIM_REFRESH = 31; + private static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 32; RuimRecords(CDMAPhone p) { super(p); @@ -93,6 +99,7 @@ public final class RuimRecords extends IccRecords { // Start off by setting empty state onRadioOffOrNotAvailable(); + p.mCM.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null); } @@ -101,6 +108,8 @@ public final class RuimRecords extends IccRecords { phone.mCM.unregisterForRUIMReady(this); phone.mCM.unregisterForOffOrNotAvailable( this); phone.mCM.unSetOnIccRefresh(this); + phone.mCM.unregisterForNVReady(this); + phone.mCM.unregisterForCdmaOtaProvision(this); } @Override @@ -134,7 +143,7 @@ public final class RuimRecords extends IccRecords { return mMyMobileNumber; } - public String getMin() { + public String getCdmaMin() { return mMin2Min1; } @@ -219,11 +228,21 @@ public final class RuimRecords extends IccRecords { if (ar.exception != null) { break; } - + if(m_ota_commited) { + if(mMyMobileNumber != localTemp[0]) { + Intent intent = new Intent(TelephonyIntents.ACTION_CDMA_OTA_MDN_CHANGED); + intent.putExtra("mdn", localTemp[0]); + Log.d(LOG_TAG,"Broadcasting intent MDN Change in OTA "); + ActivityManagerNative.broadcastStickyIntent(intent, null); + } + m_ota_commited=false; + } mMyMobileNumber = localTemp[0]; mSid = localTemp[1]; mNid = localTemp[2]; - mMin2Min1 = localTemp[3]; + if (localTemp.length >= 3) { // TODO(Moto): remove when new ril always returns min2_min1 + mMin2Min1 = localTemp[3]; + } Log.d(LOG_TAG, "MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1); @@ -272,6 +291,19 @@ public final class RuimRecords extends IccRecords { } break; + case EVENT_OTA_PROVISION_STATUS_CHANGE: + m_ota_commited=false; + ar = (AsyncResult)msg.obj; + if (ar.exception == null) { + int[] ints = (int[]) ar.result; + int otaStatus = ints[0]; + if (otaStatus== phone.CDMA_OTA_PROVISION_STATUS_COMMITTED) { + m_ota_commited=true; + phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); + } + } + break; + }}catch (RuntimeException exc) { // I don't want these exceptions to be fatal Log.w(LOG_TAG, "Exception parsing RUIM record", exc); @@ -374,17 +406,17 @@ public final class RuimRecords extends IccRecords { switch ((result[0])) { case CommandsInterface.SIM_REFRESH_FILE_UPDATED: - if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); + if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); adnCache.reset(); fetchRuimRecords(); break; case CommandsInterface.SIM_REFRESH_INIT: - if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); + if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); // need to reload all files (that we care about) fetchRuimRecords(); break; case CommandsInterface.SIM_REFRESH_RESET: - if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); + if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); phone.mCM.setRadioPower(false, null); /* Note: no need to call setRadioPower(true). Assuming the desired * radio power state is still ON (as tracked by ServiceStateTracker), @@ -396,7 +428,7 @@ public final class RuimRecords extends IccRecords { break; default: // unknown refresh operation - if (DBG) log("handleRuimRefresh with unknown operation"); + if (DBG) log("handleRuimRefresh with unknown operation"); break; } } diff --git a/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java new file mode 100644 index 0000000000000..444dec31b1ff0 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.cdma; + +import java.util.HashMap; +import java.util.HashSet; +import android.util.Log; +import android.media.ToneGenerator; + +public class SignalToneUtil { + // public final int int IS95_CONST_IR_SIGNAL_TYPE_TYPE; + static public final int IS95_CONST_IR_SIGNAL_TONE = 0; + static public final int IS95_CONST_IR_SIGNAL_ISDN = 1; + static public final int IS95_CONST_IR_SIGNAL_IS54B = 2; + static public final int IS95_CONST_IR_SIGNAL_USR_DEFD_ALERT = 4; + + // public final int int IS95_CONST_IR_ALERT_PITCH_TYPE; + static public final int IS95_CONST_IR_ALERT_MED = 0; + static public final int IS95_CONST_IR_ALERT_HIGH = 1; + static public final int IS95_CONST_IR_ALERT_LOW = 2; + static public final int TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN = 255; + + // public final int int IS95_CONST_IR_SIGNAL_TYPE; + static public final int IS95_CONST_IR_SIG_ISDN_NORMAL = 0; + static public final int IS95_CONST_IR_SIG_ISDN_INTGRP = 1; + static public final int IS95_CONST_IR_SIG_ISDN_SP_PRI = 2; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_3 = 3; + static public final int IS95_CONST_IR_SIG_ISDN_PING = 4; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_5 = 5; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_6 = 6; + static public final int IS95_CONST_IR_SIG_ISDN_PAT_7 = 7; + static public final int IS95_CONST_IR_SIG_ISDN_OFF = 15; + static public final int IS95_CONST_IR_SIG_TONE_DIAL = 0; + static public final int IS95_CONST_IR_SIG_TONE_RING = 1; + static public final int IS95_CONST_IR_SIG_TONE_INT = 2; + static public final int IS95_CONST_IR_SIG_TONE_ABB_INT = 3; + static public final int IS95_CONST_IR_SIG_TONE_REORDER = 4; + static public final int IS95_CONST_IR_SIG_TONE_ABB_RE = 5; + static public final int IS95_CONST_IR_SIG_TONE_BUSY = 6; + static public final int IS95_CONST_IR_SIG_TONE_CONFIRM = 7; + static public final int IS95_CONST_IR_SIG_TONE_ANSWER = 8; + static public final int IS95_CONST_IR_SIG_TONE_CALL_W = 9; + static public final int IS95_CONST_IR_SIG_TONE_PIP = 10; + static public final int IS95_CONST_IR_SIG_TONE_NO_TONE = 63; + static public final int IS95_CONST_IR_SIG_IS54B_NO_TONE = 0; + static public final int IS95_CONST_IR_SIG_IS54B_L = 1; + static public final int IS95_CONST_IR_SIG_IS54B_SS = 2; + static public final int IS95_CONST_IR_SIG_IS54B_SSL = 3; + static public final int IS95_CONST_IR_SIG_IS54B_SS_2 = 4; + static public final int IS95_CONST_IR_SIG_IS54B_SLS = 5; + static public final int IS95_CONST_IR_SIG_IS54B_S_X4 = 6; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_L = 7; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_SS = 8; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_SSL = 9; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_SLS = 10; + static public final int IS95_CONST_IR_SIG_IS54B_PBX_S_X4 = 11; + static public final int IS95_CONST_IR_SIG_TONE_ABBR_ALRT = 0; + + // Hashmap to map signalInfo To AudioTone + static private HashMap hm = new HashMap(); + + private static Integer signalParamHash(int signalType, int alertPitch, int signal) { + // TODO(Moto): The input should get checked before usage + return new Integer(signalType * 256 * 256 + alertPitch * 256 + signal); + } + + public static int getAudioToneFromSignalInfo(int signalType, int alertPitch, int signal) { + int result = ToneGenerator.TONE_CDMA_INVALID; + result = hm.get(signalParamHash(signalType, alertPitch, signal)); + return result; + } + + static { + + /* SIGNAL_TYPE_ISDN */ + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_NORMAL), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_NORMAL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_INTGRP), + ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_INTERGROUP); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_SP_PRI), ToneGenerator.TONE_CDMA_CALL_SIGNAL_SP_PRI); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PAT_3), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT3); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PING), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_RING_RING); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PAT_5), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT5); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PAT_6), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT6); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_PAT_7), ToneGenerator.TONE_CDMA_CALL_SIGNAL_ISDN_PAT7); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_ISDN, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_ISDN_OFF), ToneGenerator.TONE_CDMA_SIGNAL_OFF); + + /* SIGNAL_TYPE_TONE */ + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_DIAL), ToneGenerator.TONE_CDMA_DIAL_TONE_LITE); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_RING), ToneGenerator.TONE_CDMA_NETWORK_USA_RINGBACK); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_INT), ToneGenerator.TONE_SUP_INTERCEPT); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ABB_INT), ToneGenerator.TONE_SUP_INTERCEPT_ABBREV); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_REORDER), ToneGenerator.TONE_CDMA_REORDER); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ABB_RE), ToneGenerator.TONE_CDMA_ABBR_REORDER); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_BUSY), ToneGenerator.TONE_CDMA_NETWORK_BUSY); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_CONFIRM), ToneGenerator.TONE_SUP_CONFIRM); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ANSWER), ToneGenerator.TONE_CDMA_ANSWER); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_ABB_RE), ToneGenerator.TONE_CDMA_NETWORK_CALLWAITING); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_PIP), ToneGenerator.TONE_CDMA_PIP); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_TONE, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_TONE_NO_TONE), ToneGenerator.TONE_CDMA_SIGNAL_OFF); + + /* SIGNAL_TYPE_IS54B */ + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_HIGH_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_INVALID); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_L), ToneGenerator.TONE_CDMA_LOW_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SS), ToneGenerator.TONE_CDMA_HIGH_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SS), ToneGenerator.TONE_CDMA_MED_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SS), ToneGenerator.TONE_CDMA_LOW_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SSL), ToneGenerator.TONE_CDMA_HIGH_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SSL), ToneGenerator.TONE_CDMA_MED_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SSL), ToneGenerator.TONE_CDMA_LOW_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SS_2), ToneGenerator.TONE_CDMA_HIGH_SS_2); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SS_2), ToneGenerator.TONE_CDMA_MED_SS_2); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SS_2), ToneGenerator.TONE_CDMA_LOW_SS_2); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_SLS), ToneGenerator.TONE_CDMA_HIGH_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_SLS), ToneGenerator.TONE_CDMA_MED_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_SLS), ToneGenerator.TONE_CDMA_LOW_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_S_X4), ToneGenerator.TONE_CDMA_HIGH_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_S_X4), ToneGenerator.TONE_CDMA_MED_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_S_X4), ToneGenerator.TONE_CDMA_LOW_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_L), ToneGenerator.TONE_CDMA_HIGH_PBX_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_L), ToneGenerator.TONE_CDMA_MED_PBX_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_L), ToneGenerator.TONE_CDMA_LOW_PBX_L); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_SS), ToneGenerator.TONE_CDMA_HIGH_PBX_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_SS), ToneGenerator.TONE_CDMA_MED_PBX_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_SS), ToneGenerator.TONE_CDMA_LOW_PBX_SS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_SSL), ToneGenerator.TONE_CDMA_HIGH_PBX_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_SSL), ToneGenerator.TONE_CDMA_MED_PBX_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_SSL), ToneGenerator.TONE_CDMA_LOW_PBX_SSL); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_SLS), ToneGenerator.TONE_CDMA_HIGH_PBX_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_SLS), ToneGenerator.TONE_CDMA_MED_PBX_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_SLS), ToneGenerator.TONE_CDMA_LOW_PBX_SLS); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_HIGH, + IS95_CONST_IR_SIG_IS54B_PBX_S_X4), ToneGenerator.TONE_CDMA_HIGH_PBX_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_MED, + IS95_CONST_IR_SIG_IS54B_PBX_S_X4), ToneGenerator.TONE_CDMA_MED_PBX_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, IS95_CONST_IR_ALERT_LOW, + IS95_CONST_IR_SIG_IS54B_PBX_S_X4), ToneGenerator.TONE_CDMA_LOW_PBX_S_X4); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_IS54B, TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, + IS95_CONST_IR_SIG_IS54B_NO_TONE), ToneGenerator.TONE_CDMA_SIGNAL_OFF); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_USR_DEFD_ALERT, + TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, IS95_CONST_IR_SIG_TONE_ABBR_ALRT), + ToneGenerator.TONE_CDMA_ABBR_ALERT); + + hm.put(signalParamHash(IS95_CONST_IR_SIGNAL_USR_DEFD_ALERT, + TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN, IS95_CONST_IR_SIG_TONE_NO_TONE), + ToneGenerator.TONE_CDMA_ABBR_ALERT); + + } + + // suppress default constructor for noninstantiability + private SignalToneUtil() { + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 3459dcdf23865..70d71fc83f443 100755 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -878,11 +878,6 @@ public class GSMPhone extends PhoneBase { return ret; } - public String getMin() { - Log.e(LOG_TAG, "[GSMPhone] getMin() is a CDMA method"); - return "0"; - } - public String getDeviceId() { return mImei; } diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index 42bb2e0c33768..19679aa156020 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -1137,7 +1137,7 @@ public final class SimulatedCommands extends BaseCommands String number, Message result) {unimplemented(result);} public void setNetworkSelectionModeAutomatic(Message result) {unimplemented(result);} - + public void exitEmergencyCallbackMode(Message result) {unimplemented(result);} public void setNetworkSelectionModeManual( String operatorNumeric, Message result) {unimplemented(result);} @@ -1460,10 +1460,6 @@ public final class SimulatedCommands extends BaseCommands } - public void exitEmergencyCallbackMode(Message response) { - // TODO method stub - } - public void forceDataDormancy(Message response) { // TODO method stub } From f3ede869b8abef8d85e204bc259ee0cf8dc8d2ae Mon Sep 17 00:00:00 2001 From: Yang Li Date: Thu, 28 May 2009 18:00:50 -0700 Subject: [PATCH 23/43] Converted the angle of OrientedBoundingBox to degrees --- core/java/android/gesture/GestureUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java index 3e8a3c836712c..40d70295fc9b2 100755 --- a/core/java/android/gesture/GestureUtilities.java +++ b/core/java/android/gesture/GestureUtilities.java @@ -413,7 +413,7 @@ final class GestureUtilities { } } - return new OrientedBoundingBox(angle, centroid[0], centroid[1], maxx - minx, maxy - miny); + return new OrientedBoundingBox((float) (angle * 180 / Math.PI), centroid[0], centroid[1], maxx - minx, maxy - miny); } private static double[] computeOrientation(double[][] covarianceMatrix) { From 5f123bdcde61b9fdcd58952882c621c34a727e10 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Thu, 28 May 2009 18:06:31 -0700 Subject: [PATCH 24/43] Making sure non-public API is marked with @hide. --- core/java/android/provider/ContactsContract.java | 2 +- core/java/android/provider/SocialContract.java | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 0485a1083388c..cc71547c83843 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -23,7 +23,7 @@ import android.net.Uri; * The contract between the contacts provider and applications. Contains definitions * for the supported URIs and columns. * - * TODO: move to android.provider + * @hide */ public final class ContactsContract { /** The authority for the contacts provider */ diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java index 8254851d2d361..d4a9c489f72c8 100644 --- a/core/java/android/provider/SocialContract.java +++ b/core/java/android/provider/SocialContract.java @@ -16,17 +16,14 @@ package android.provider; -import android.provider.ContactsContract.Contacts; - import android.graphics.BitmapFactory; import android.net.Uri; -import android.provider.BaseColumns; /** * The contract between the social provider and applications. Contains * definitions for the supported URIs and columns. - *

    - * TODO: move to android.provider package + * + * @hide */ public class SocialContract { /** The authority for the social provider */ From 51e03642cee5a695d6ff6f5b318925efcab05100 Mon Sep 17 00:00:00 2001 From: Brett Chabot Date: Thu, 28 May 2009 18:18:15 -0700 Subject: [PATCH 25/43] Change code coverage to write to app data folder rather than sdcard. The InstrumentationTestRunner would previously write code coverage data to the sdcard. With the recent SDCARD_WRITE permission addition, generating code coverage would fail if test app did not declare the SDCARD_WRITE permission. This CL changes InstrumentationTestRunner so by default, the coverage data is saved to the app's private data folder. At the test run conclusion it outputs the path to this file so runtest or another test harness find the path to the coverage file. --- .../test/InstrumentationTestRunner.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java index d5e64596c7319..6658fb0732130 100644 --- a/test-runner/android/test/InstrumentationTestRunner.java +++ b/test-runner/android/test/InstrumentationTestRunner.java @@ -118,7 +118,8 @@ import java.util.List; * To generate EMMA code coverage: * -e coverage true * Note: this requires an emma instrumented build. By default, the code coverage results file - * will be saved as /sdcard/coverage.ec, unless overridden by coverageFile flag (see below) + * will be saved in a /data//coverage.ec file, unless overridden by coverageFile flag (see + * below) *

    * To specify EMMA code coverage results file path: * -e coverageFile /sdcard/myFile.ec @@ -217,6 +218,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu * reports the guessed suite assignment for the current test. */ private static final String REPORT_KEY_SUITE_ASSIGNMENT = "suiteassignment"; + /** + * If included in the status or final bundle sent to an IInstrumentationWatcher, this key + * identifies the path to the generated code coverage file. + */ + private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath"; /** * The test is starting. */ @@ -240,7 +246,8 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu */ public static final String REPORT_KEY_STACK = "stack"; - private static final String DEFAULT_COVERAGE_FILE_PATH = "/sdcard/coverage.ec"; + // Default file name for code coverage + private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec"; private static final String LOG_TAG = "InstrumentationTestRunner"; @@ -456,14 +463,20 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private void generateCoverageReport() { // use reflection to call emma dump coverage method, to avoid // always statically compiling against emma jar - java.io.File coverageFile = new java.io.File(getCoverageFilePath()); + String coverageFilePath = getCoverageFilePath(); + java.io.File coverageFile = new java.io.File(coverageFilePath); try { Class emmaRTClass = Class.forName("com.vladium.emma.rt.RT"); Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData", coverageFile.getClass(), boolean.class, boolean.class); dumpCoverageMethod.invoke(null, coverageFile, false, false); - + // output path to generated coverage file so it can be parsed by a test harness if + // needed + mResults.putString(REPORT_KEY_COVERAGE_PATH, coverageFilePath); + // also output a more user friendly msg + mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, + String.format("Generated code coverage data to %s", coverageFilePath)); } catch (ClassNotFoundException e) { reportEmmaError("Is emma jar on classpath?", e); } catch (SecurityException e) { @@ -481,8 +494,9 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu private String getCoverageFilePath() { if (mCoverageFilePath == null) { - return DEFAULT_COVERAGE_FILE_PATH; - } + return getTargetContext().getFilesDir().getAbsolutePath() + File.separator + + DEFAULT_COVERAGE_FILE_NAME; + } else { return mCoverageFilePath; } From 01c0596b75388e3df7cb4fb022d904c4cac9d831 Mon Sep 17 00:00:00 2001 From: Yu Shan Emily Lau Date: Thu, 28 May 2009 15:41:16 -0700 Subject: [PATCH 26/43] Added the two validations for the memory stress test 1) Set the maximum memory leakage to 150K in 200 loops of playback. 2) Check the pid of the meidaserver. --- .../performance/MediaPlayerPerformance.java | 106 ++++++++++++++++-- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index 2f0173da8db2f..4a2f8236161e7 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -68,6 +68,13 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase Date: Thu, 28 May 2009 21:12:01 -0700 Subject: [PATCH 27/43] Revert "Revert "Bug fixes and performance improvements"" This reverts commit 436466d75edb5f6fd848504d998f244426ea5a09. --- core/java/android/gesture/GestureLibrary.java | 10 +++-- core/java/android/gesture/GestureStroke.java | 11 ++--- .../android/gesture/GestureUtilities.java | 44 +++++++++++++++---- core/java/android/gesture/Instance.java | 32 +++++++------- .../java/android/gesture/InstanceLearner.java | 9 +--- 5 files changed, 65 insertions(+), 41 deletions(-) diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java index 1cf192ec34461..9020d2bafc056 100644 --- a/core/java/android/gesture/GestureLibrary.java +++ b/core/java/android/gesture/GestureLibrary.java @@ -136,7 +136,7 @@ public class GestureLibrary { * @return a list of predictions of possible entries for a given gesture */ public ArrayList recognize(Gesture gesture) { - Instance instance = Instance.createInstance(mSequenceType, gesture, null); + Instance instance = Instance.createInstance(mSequenceType, mOrientationStyle, gesture, null); return mClassifier.classify(mSequenceType, instance.vector); } @@ -156,7 +156,7 @@ public class GestureLibrary { mNamedGestures.put(entryName, gestures); } gestures.add(gesture); - mClassifier.addInstance(Instance.createInstance(mSequenceType, gesture, entryName)); + mClassifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName)); mChanged = true; } @@ -337,10 +337,14 @@ public class GestureLibrary { for (int j = 0; j < gestureCount; j++) { final Gesture gesture = Gesture.deserialize(in); gestures.add(gesture); - classifier.addInstance(Instance.createInstance(mSequenceType, gesture, name)); + classifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name)); } namedGestures.put(name, gestures); } } + + Learner getLearner() { + return mClassifier; + } } diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java index 0d7bc2d049d4e..90faaed6430f4 100644 --- a/core/java/android/gesture/GestureStroke.java +++ b/core/java/android/gesture/GestureStroke.java @@ -17,7 +17,6 @@ package android.gesture; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; @@ -147,10 +146,12 @@ public class GestureStroke { final float[] pts = GestureUtilities.temporalSampling(this, numSample); final RectF rect = boundingBox; - final Matrix matrix = new Matrix(); - matrix.setTranslate(-rect.left, -rect.top); - matrix.postScale(width / rect.width(), height / rect.height()); - matrix.mapPoints(pts); + GestureUtilities.translate(pts, -rect.left, -rect.top); + + float sx = width / rect.width(); + float sy = height / rect.height(); + float scale = sx > sy ? sy : sx; + GestureUtilities.scale(pts, scale, scale); float mX = 0; float mY = 0; diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtilities.java index 4a3144c2f0b74..3e8a3c836712c 100755 --- a/core/java/android/gesture/GestureUtilities.java +++ b/core/java/android/gesture/GestureUtilities.java @@ -17,7 +17,6 @@ package android.gesture; import android.graphics.RectF; -import android.graphics.Matrix; import android.util.Log; import java.util.ArrayList; @@ -380,22 +379,17 @@ final class GestureUtilities { } static OrientedBoundingBox computeOrientedBoundingBox(float[] points, float[] centroid) { - Matrix tr = new Matrix(); - tr.setTranslate(-centroid[0], -centroid[1]); - tr.mapPoints(points); + translate(points, -centroid[0], -centroid[1]); double[][] array = computeCoVariance(points); double[] targetVector = computeOrientation(array); float angle; if (targetVector[0] == 0 && targetVector[1] == 0) { - angle = -90; + angle = (float) -Math.PI/2; } else { // -PI classify(int gestureType, float[] vector) { + ArrayList classify(int sequenceType, float[] vector) { ArrayList predictions = new ArrayList(); ArrayList instances = getInstances(); int count = instances.size(); @@ -43,7 +38,7 @@ class InstanceLearner extends Learner { continue; } double distance; - if (gestureType == GestureLibrary.SEQUENCE_SENSITIVE) { + if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) { distance = GestureUtilities.cosineDistance(sample.vector, vector); } else { distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector); From 9fc2e9c965c68d56a0caf812f7f6d38d15317063 Mon Sep 17 00:00:00 2001 From: Bjorn Bringert Date: Thu, 28 May 2009 14:48:32 +0100 Subject: [PATCH 28/43] MemoryFile constructor and native methods throw IOExceptions. These native methods in android.os.MemoryFile throw IOException but their Java declarations did not include "throws IOException": native_open(),native_mmap(),native_read(),native_write(),native_pin() The MemoryFile(String,int) constructor calls native_open and native_mmap, but does not declare that it throws IOException. The other Java methods that call the native methods do actually declare that they throw IOException. This means that any code that created memory files could throw an IOException, without knowing about it. This changes adds "throws IOException" to the native methods and to the constructor. The constructor change changes the public API, but maintains binary compatibility. There is some precedent for making source incompatible source API changes for this sort of thing (see https://mondrian.corp.google.com/changelist/124214-p9). The change also makes the native methods static, which they seem to have been intended to be, as indicated by the second parameter to the native implementations being named "clazz". This requires changes to the Compatibility Test Suite to catch the exceptions. This is done in https://android-git.corp.google.com/g/2617 Unfortunately that change must be submitted together with this one in order not to break the build. Fixes http://b/issue?id=1881829 --- api/current.xml | 2 ++ core/java/android/os/MemoryFile.java | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/api/current.xml b/api/current.xml index bf3044e30fce3..c67c9c4218597 100644 --- a/api/current.xml +++ b/api/current.xml @@ -93802,6 +93802,8 @@ + + Date: Fri, 29 May 2009 02:03:21 -0700 Subject: [PATCH 29/43] Fix issue 1883666: Audio coming from the music player stopped suddenly The problem comes from the code handling the automatic change of audio routing to speaker when notifications are played. The music is also muted while the sound is forced to speaker. To avoid truncating the end of the notification, a delay is inserted between the end of the notification and the restoration of the audio routing. If a new notification starts during this delay, the current music mute state read and saved before muting music corresponds to the forced mute due to previous notification. When the new notification ends, the mute state restored is muted and music stream stays muted for ever. The fix consists in reading and saving music mute state only if the audio routing has been restored (check that mForcedRoute is back to 0). --- libs/audioflinger/AudioFlinger.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 13e457faa3b06..324111bd63abb 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -817,19 +817,22 @@ void AudioFlinger::handleForcedSpeakerRoute(int command) { AutoMutex lock(mHardwareLock); if (mForcedSpeakerCount++ == 0) { - mRouteRestoreTime = 0; - mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); - if (mForcedRoute == 0 && !(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { - LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); - usleep(mHardwareMixerThread->latency()*1000); - mHardwareStatus = AUDIO_HW_SET_ROUTING; - mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareStatus = AUDIO_HW_IDLE; - // delay track start so that audio hardware has time to siwtch routes - usleep(kStartSleepTime); + if (mForcedRoute == 0) { + mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); + LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime); + if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { + LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); + usleep(mHardwareMixerThread->latency()*1000); + mHardwareStatus = AUDIO_HW_SET_ROUTING; + mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); + mHardwareStatus = AUDIO_HW_IDLE; + // delay track start so that audio hardware has time to siwtch routes + usleep(kStartSleepTime); + } } mForcedRoute = AudioSystem::ROUTE_SPEAKER; + mRouteRestoreTime = 0; } LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); } From 761e0918d30b6a3f292625b44b86dffd1538bc78 Mon Sep 17 00:00:00 2001 From: Bjorn Bringert Date: Fri, 29 May 2009 11:46:12 +0100 Subject: [PATCH 30/43] Unmap memory in MemoryFile.close(). As reported in http://b/issue?id=1398215 MemoryFile did not munmap(2) the ashmem region after closing it. This causes the process to leak virtual address space. This change fixes the problem by calling munmap(2) in close(). The unmapping is done by a helper method deactivate(). The change also replaces the use of an int for the file descriptor with a FileDescriptor object to make sure that we keep track of when the file descriptor has been closed. I chose to implement it this way because I will need decativate() and a FileDescriptor object in an upcoming change that allows sending MemoryFile file descriptors between processes. The change also adds a number of tests for the behavior of close(). The testCloseRead() and testCloseWrite() fail with the old MemoryFile implementation, and testCloseLeak() causes a segfault. They all pass now. --- core/java/android/os/MemoryFile.java | 65 ++++++++++++---- core/jni/android_os_MemoryFile.cpp | 50 ++++++++---- .../android/unit_tests/os/MemoryFileTest.java | 77 ++++++++++++++++++- 3 files changed, 157 insertions(+), 35 deletions(-) diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java index 2e4d9b1ade02b..65e83c77957a3 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -18,6 +18,7 @@ package android.os; import android.util.Log; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -35,19 +36,19 @@ import java.io.OutputStream; public class MemoryFile { private static String TAG = "MemoryFile"; - - // returns fd - private static native int native_open(String name, int length) throws IOException; - // returns memory address for ashmem region - private static native int native_mmap(int fd, int length) throws IOException; - private static native void native_close(int fd); - private static native int native_read(int fd, int address, byte[] buffer, - int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; - private static native void native_write(int fd, int address, byte[] buffer, - int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; - private static native void native_pin(int fd, boolean pin) throws IOException; - private int mFD; // ashmem file descriptor + private static native FileDescriptor native_open(String name, int length) throws IOException; + // returns memory address for ashmem region + private static native int native_mmap(FileDescriptor fd, int length) throws IOException; + private static native void native_munmap(int addr, int length) throws IOException; + private static native void native_close(FileDescriptor fd); + private static native int native_read(FileDescriptor fd, int address, byte[] buffer, + int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; + private static native void native_write(FileDescriptor fd, int address, byte[] buffer, + int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; + private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException; + + private FileDescriptor mFD; // ashmem file descriptor private int mAddress; // address of ashmem memory private int mLength; // total length of our ashmem region private boolean mAllowPurging = false; // true if our ashmem region is unpinned @@ -69,15 +70,40 @@ public class MemoryFile * Closes and releases all resources for the memory file. */ public void close() { - if (mFD > 0) { + deactivate(); + if (!isClosed()) { native_close(mFD); - mFD = 0; } } + private void deactivate() { + if (!isDeactivated()) { + try { + native_munmap(mAddress, mLength); + mAddress = 0; + } catch (IOException ex) { + Log.e(TAG, ex.toString()); + } + } + } + + /** + * Checks whether the memory file has been deactivated. + */ + private boolean isDeactivated() { + return mAddress == 0; + } + + /** + * Checks whether the memory file has been closed. + */ + private boolean isClosed() { + return !mFD.valid(); + } + @Override protected void finalize() { - if (mFD > 0) { + if (!isClosed()) { Log.e(TAG, "MemoryFile.finalize() called while ashmem still open"); close(); } @@ -132,7 +158,6 @@ public class MemoryFile @return OutputStream */ public OutputStream getOutputStream() { - return new MemoryOutputStream(); } @@ -145,9 +170,13 @@ public class MemoryFile * @param destOffset offset into the byte array buffer to read into. * @param count number of bytes to read. * @return number of bytes read. + * @throws IOException if the memory file has been purged or deactivated. */ public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { + if (isDeactivated()) { + throw new IOException("Can't read from deactivated memory file."); + } if (destOffset < 0 || destOffset > buffer.length || count < 0 || count > buffer.length - destOffset || srcOffset < 0 || srcOffset > mLength @@ -165,9 +194,13 @@ public class MemoryFile * @param srcOffset offset into the byte array buffer to write from. * @param destOffset offset into the memory file to write to. * @param count number of bytes to write. + * @throws IOException if the memory file has been purged or deactivated. */ public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { + if (isDeactivated()) { + throw new IOException("Can't write to deactivated memory file."); + } if (srcOffset < 0 || srcOffset > buffer.length || count < 0 || count > buffer.length - srcOffset || destOffset < 0 || destOffset > mLength diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp index edf7dc45991b9..9450e158dee55 100644 --- a/core/jni/android_os_MemoryFile.cpp +++ b/core/jni/android_os_MemoryFile.cpp @@ -26,7 +26,7 @@ namespace android { -static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) +static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) { const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL); @@ -37,28 +37,45 @@ static jint android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, if (name) env->ReleaseStringUTFChars(name, namestr); - if (result < 0) + if (result < 0) { jniThrowException(env, "java/io/IOException", "ashmem_create_region failed"); - return result; + return NULL; + } + + return jniCreateFileDescriptor(env, result); } -static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jint fd, jint length) +static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor, + jint length) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); jint result = (jint)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (!result) jniThrowException(env, "java/io/IOException", "mmap failed"); return result; } -static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jint fd) +static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jint addr, jint length) { - close(fd); + int result = munmap((void *)addr, length); + if (result < 0) + jniThrowException(env, "java/io/IOException", "munmap failed"); +} + +static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (fd >= 0) { + jniSetFileDescriptorOfFD(env, fileDescriptor, -1); + close(fd); + } } static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, - jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); @@ -76,9 +93,10 @@ static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, } static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, - jint fd, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, + jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); @@ -95,8 +113,9 @@ static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, return count; } -static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jint fd, jboolean pin) +static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin) { + int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0)); if (result < 0) { jniThrowException(env, "java/io/IOException", NULL); @@ -104,12 +123,13 @@ static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jint fd, jbool } static const JNINativeMethod methods[] = { - {"native_open", "(Ljava/lang/String;I)I", (void*)android_os_MemoryFile_open}, - {"native_mmap", "(II)I", (void*)android_os_MemoryFile_mmap}, - {"native_close", "(I)V", (void*)android_os_MemoryFile_close}, - {"native_read", "(II[BIIIZ)I", (void*)android_os_MemoryFile_read}, - {"native_write", "(II[BIIIZ)V", (void*)android_os_MemoryFile_write}, - {"native_pin", "(IZ)V", (void*)android_os_MemoryFile_pin}, + {"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open}, + {"native_mmap", "(Ljava/io/FileDescriptor;I)I", (void*)android_os_MemoryFile_mmap}, + {"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap}, + {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close}, + {"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read}, + {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write}, + {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin}, }; static const char* const kClassPathName = "android/os/MemoryFile"; diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java index 508afcf411b81..5161f7beda0e4 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java @@ -17,16 +17,17 @@ package com.android.unit_tests.os; import android.os.MemoryFile; +import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; -import junit.framework.TestCase; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.IOException; - -import java.util.List; import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; public class MemoryFileTest extends TestCase { @@ -94,6 +95,74 @@ public class MemoryFileTest extends TestCase { file.close(); } + // Tests that close() is idempotent + @SmallTest + public void testCloseClose() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + byte[] data = new byte[512]; + file.writeBytes(data, 0, 0, 128); + file.close(); + file.close(); + } + + // Tests that we can't read from a closed memory file + @SmallTest + public void testCloseRead() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + file.close(); + + try { + byte[] data = new byte[512]; + assertEquals(128, file.readBytes(data, 0, 0, 128)); + fail("readBytes() after close() did not throw IOException."); + } catch (IOException e) { + // this is what should happen + } + } + + // Tests that we can't write to a closed memory file + @SmallTest + public void testCloseWrite() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + file.close(); + + try { + byte[] data = new byte[512]; + file.writeBytes(data, 0, 0, 128); + fail("writeBytes() after close() did not throw IOException."); + } catch (IOException e) { + // this is what should happen + } + } + + // Tests that we can't call allowPurging() after close() + @SmallTest + public void testCloseAllowPurging() throws Exception { + MemoryFile file = new MemoryFile("MemoryFileTest", 1000000); + byte[] data = new byte[512]; + file.writeBytes(data, 0, 0, 128); + file.close(); + + try { + file.allowPurging(true); + fail("allowPurging() after close() did not throw IOException."); + } catch (IOException e) { + // this is what should happen + } + } + + // Tests that we don't leak file descriptors or mmap areas + @LargeTest + public void testCloseLeak() throws Exception { + // open enough memory files that we should run out of + // file descriptors or address space if we leak either. + for (int i = 0; i < 1025; i++) { + MemoryFile file = new MemoryFile("MemoryFileTest", 5000000); + file.writeBytes(testString, 0, 0, testString.length); + file.close(); + } + } + private static final byte[] testString = new byte[] { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4, 5, 9, 2, 3, 0, 7, 8, 1, 6, 4, 0, 6, 2, 8, 6, 2, 0, 8, 9, 9, 8, 6, 2, 8, 0, 3, 4, 8, 2, 5, 3, 4, 2, 1, 1, 7, 0, 6, 7, 9, 8, 2, 1, 4, 8, 0, 8, 6, 5, 1, 3, 2, 8, 2, 3, 0, 6, 6, 4, 7, 0, 9, 3, 8, 4, 4, 6, 0, 9, 5, 5, 0, 5, 8, 2, 2, 3, 1, 7, 2, From e32edc614e62ac874a969d3cc6bb1e0c0c3f2607 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 29 May 2009 10:33:36 -0700 Subject: [PATCH 31/43] Fixes #1884152. This change improves how the opaque property is handled with respect to dividers. If the list is opaque and its background is not, then we want to fill a solid rect where the dividers should be when they are skipped for non-selectable items. When the list is opaque and the background is also opaque, there is no need to draw something in lieu of the dividers since the background will do it for us. --- core/java/android/widget/ListView.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 10d8f55cc6cb6..99cf6f8c6b5cd 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -2851,9 +2851,15 @@ public class ListView extends AbsListView { final int first = mFirstPosition; final boolean areAllItemsSelectable = mAreAllItemsSelectable; final ListAdapter adapter = mAdapter; - final boolean isOpaque = isOpaque(); - if (isOpaque && mDividerPaint == null) { + // If the list is opaque *and* the background is not, we want to + // fill a rect where the dividers would be for non-selectable items + // If the list is opaque and the background is also opaque, we don't + // need to draw anything since the background will do it for us + final boolean fillForMissingDividers = isOpaque() && !super.isOpaque(); + + if (fillForMissingDividers && mDividerPaint == null && mIsCacheColorOpaque) { mDividerPaint = new Paint(); + mDividerPaint.setColor(getCacheColorHint()); } final Paint paint = mDividerPaint; @@ -2874,7 +2880,7 @@ public class ListView extends AbsListView { bounds.top = bottom; bounds.bottom = bottom + dividerHeight; drawDivider(canvas, bounds, i); - } else if (isOpaque) { + } else if (fillForMissingDividers) { bounds.top = bottom; bounds.bottom = bottom + dividerHeight; canvas.drawRect(bounds, paint); @@ -2903,7 +2909,7 @@ public class ListView extends AbsListView { // position. Give -1 when there is no child above the // divider. drawDivider(canvas, bounds, i - 1); - } else if (isOpaque) { + } else if (fillForMissingDividers) { bounds.top = top - dividerHeight; bounds.bottom = top; canvas.drawRect(bounds, paint); From 45515659438964ec47f4feac247f0e9dce587c86 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Thu, 23 Apr 2009 15:20:21 -0700 Subject: [PATCH 32/43] Enforce permissions for PhoneStateListener events. PhoneStateListener events like LISTEN_CALL_STATE_CHANGED, have privacy information like phone numbers and hence, need to be protected with a permission. The permission READ_PHONE_STATE is used for this purpose. Use the permission trick to ensure backward compatability. --- .../android/content/pm/PackageParser.java | 10 ++-- .../com/android/server/TelephonyRegistry.java | 58 +++++++++++-------- .../android/telephony/PhoneStateListener.java | 18 +++++- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 78462f1a25d0a..ee20aeebb0a77 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -74,10 +74,12 @@ public class PackageParser { * added to older SDKs appearing before those added to newer SDKs. * @hide */ - public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = new PackageParser.NewPermissionInfo[] { - new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_SDCARD, - android.os.Build.VERSION_CODES.DONUT, - 0) + public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = + new PackageParser.NewPermissionInfo[] { + new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_SDCARD, + android.os.Build.VERSION_CODES.DONUT, 0), + new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE, + android.os.Build.VERSION_CODES.DONUT, 0) }; private String mArchiveSourcePath; diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index 88f47fda431fe..b601ecea6716c 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -92,6 +92,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private Bundle mCellLocation = new Bundle(); + static final int PHONE_STATE_PERMISSION_MASK = + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | + PhoneStateListener.LISTEN_CALL_STATE | + PhoneStateListener.LISTEN_DATA_ACTIVITY | + PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | + PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR; + // we keep a copy of all of the state so we can send it out when folks // register for it // @@ -110,16 +117,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { // Log.d(TAG, "listen pkg=" + pkgForDebug + " events=0x" + // Integer.toHexString(events)); if (events != 0) { - // check permissions - if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { - // ACCESS_FINE_LOCATION implies ACCESS_COARSE_LOCATION - if (mContext.checkCallingPermission( - android.Manifest.permission.ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_COARSE_LOCATION, null); - } - } + /* Checks permission and throws Security exception */ + checkListenerPermission(events); synchronized (mRecords) { // register @@ -219,7 +218,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallState(int state, String incomingNumber) { - if (!checkPhoneStatePermission("notifyCallState()")) { + if (!checkNotifyPermission("notifyCallState()")) { return; } synchronized (mRecords) { @@ -240,7 +239,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyServiceState(ServiceState state) { - if (!checkPhoneStatePermission("notifyServiceState()")) { + if (!checkNotifyPermission("notifyServiceState()")){ return; } synchronized (mRecords) { @@ -256,7 +255,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifySignalStrength(SignalStrength signalStrength) { - if (!checkPhoneStatePermission("notifySignalStrength()")) { + if (!checkNotifyPermission("notifySignalStrength()")) { return; } synchronized (mRecords) { @@ -281,7 +280,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyMessageWaitingChanged(boolean mwi) { - if (!checkPhoneStatePermission("notifyMessageWaitingChanged()")) { + if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { return; } synchronized (mRecords) { @@ -300,7 +299,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCallForwardingChanged(boolean cfi) { - if (!checkPhoneStatePermission("notifyCallForwardingChanged()")) { + if (!checkNotifyPermission("notifyCallForwardingChanged()")) { return; } synchronized (mRecords) { @@ -319,7 +318,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataActivity(int state) { - if (!checkPhoneStatePermission("notifyDataActivity()")) { + if (!checkNotifyPermission("notifyDataActivity()" )) { return; } synchronized (mRecords) { @@ -337,9 +336,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyDataConnection(int state, boolean isDataConnectivityPossible, String reason, - String apn, String interfaceName) { - if (!checkPhoneStatePermission("notifyDataConnection()")) { + public void notifyDataConnection(int state, boolean isDataConnectivityPossible, + String reason, String apn, String interfaceName) { + if (!checkNotifyPermission("notifyDataConnection()" )) { return; } synchronized (mRecords) { @@ -364,7 +363,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataConnectionFailed(String reason) { - if (!checkPhoneStatePermission("notifyDataConnectionFailed()")) { + if (!checkNotifyPermission("notifyDataConnectionFailed()")) { return; } /* @@ -385,7 +384,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyCellLocation(Bundle cellLocation) { - if (!checkPhoneStatePermission("notifyCellLocation()")) { + if (!checkNotifyPermission("notifyCellLocation()")) { return; } synchronized (mRecords) { @@ -402,7 +401,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { /** * Copy the service state object so they can't mess it up in the local calls */ - private void sendServiceState(Record r, ServiceState state) { + public void sendServiceState(Record r, ServiceState state) { try { r.callback.onServiceStateChanged(new ServiceState(state)); } catch (RemoteException ex) { @@ -533,7 +532,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendStickyBroadcast(intent); } - private boolean checkPhoneStatePermission(String method) { + private boolean checkNotifyPermission(String method) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { return true; @@ -543,4 +542,17 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { Log.w(TAG, msg); return false; } + + private void checkListenerPermission(int events) { + if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_COARSE_LOCATION, null); + + } + + if ((events & PHONE_STATE_PERMISSION_MASK) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.READ_PHONE_STATE, null); + } + } } diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 8abafae302873..e113680b4e69e 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -42,6 +42,9 @@ public class PhoneStateListener { /** * Listen for changes to the network signal strength (cellular). + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} *

    * * @see #onSignalStrengthChanged @@ -52,6 +55,9 @@ public class PhoneStateListener { /** * Listen for changes to the message-waiting indicator. + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} *

    * Example: The status bar uses this to determine when to display the * voicemail icon. @@ -62,7 +68,9 @@ public class PhoneStateListener { /** * Listen for changes to the call-forwarding indicator. - * + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * @see #onCallForwardingIndicatorChanged */ public static final int LISTEN_CALL_FORWARDING_INDICATOR = 0x00000008; @@ -85,7 +93,9 @@ public class PhoneStateListener { /** * Listen for changes to the device call state. - * + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * @see #onCallStateChanged */ public static final int LISTEN_CALL_STATE = 0x00000020; @@ -100,7 +110,9 @@ public class PhoneStateListener { /** * Listen for changes to the direction of data traffic on the data * connection (cellular). - * + * {@more} + * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE + * READ_PHONE_STATE} * Example: The status bar uses this to display the appropriate * data-traffic icon. * From c51ef9702d7ccd3dac15ccaeb509d1150f0a2d7f Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 29 May 2009 12:05:11 -0700 Subject: [PATCH 33/43] Fixes #1873537. Only update the popup window when it is shown. This avoids throwing an NPE in PopupWindow. A PopupWindow is only aware of its content view after being shown. --- core/java/android/widget/AbsListView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 20e2c4688677b..f60aba35883fa 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1570,7 +1570,7 @@ public abstract class AbsListView extends AdapterView implements Te if (mInstallGesturesOverlay) { installGesturesOverlay(); positionGesturesPopup(); - } else if (mGesturesPopup != null) { + } else if (mGesturesPopup != null && mGesturesPopup.isShowing()) { mGesturesPopup.update(w, h); } } From 9ffe1ae56bf495b1ada1e9c1da523db0677d98b7 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 29 May 2009 12:28:23 -0700 Subject: [PATCH 34/43] Fix for #1878497. Always pre-allocate a gesture's bounding box to avoid possible NPEs. --- core/java/android/gesture/Gesture.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java index 6aca105825e81..8e71c5744d41e 100755 --- a/core/java/android/gesture/Gesture.java +++ b/core/java/android/gesture/Gesture.java @@ -46,7 +46,7 @@ public class Gesture implements Parcelable { private static int sGestureCount = 0; - private RectF mBoundingBox; + private final RectF mBoundingBox = new RectF(); // the same as its instance ID private long mGestureID; @@ -83,12 +83,7 @@ public class Gesture implements Parcelable { */ public void addStroke(GestureStroke stroke) { mStrokes.add(stroke); - - if (mBoundingBox == null) { - mBoundingBox = new RectF(stroke.boundingBox); - } else { - mBoundingBox.union(stroke.boundingBox); - } + mBoundingBox.union(stroke.boundingBox); } /** From 8b0b174198793cabb2b3fcc015f9bfdc9d5082b5 Mon Sep 17 00:00:00 2001 From: Dave Sparks Date: Fri, 29 May 2009 09:01:20 -0700 Subject: [PATCH 35/43] Update MediaPlayer to allow setVideoSurface calls after prepare. Also allow passing a null surface. The API is now enabled to change the surface while the video is playing. This could allow orientation changes during playback or to allow the audio track from a video to play in the background. NOTE: There are still changes required to pmem driver to allow remapping shared physical memory into a process in order for this to work. This change only enables the API to send the appropriate calls when the lower level code supports it. --- media/java/android/media/MediaPlayer.java | 12 ++++++++- media/jni/android_media_MediaPlayer.cpp | 30 +++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 19ab0add2b914..d3743e6607b8a 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -468,6 +468,11 @@ public class MediaPlayer */ native_setup(new WeakReference(this)); } + + /* + * Update the MediaPlayer ISurface. Call after updating mSurface. + */ + private native void _setVideoSurface(); /** * Sets the SurfaceHolder to use for displaying the video portion of the media. @@ -478,7 +483,12 @@ public class MediaPlayer */ public void setDisplay(SurfaceHolder sh) { mSurfaceHolder = sh; - mSurface = sh.getSurface(); + if (sh != null) { + mSurface = sh.getSurface(); + } else { + mSurface = null; + } + _setVideoSurface(); updateSurfaceScreenOn(); } diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 707db02fd9488..5abe451ca135c 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -198,6 +198,27 @@ android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fil process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." ); } +static void setVideoSurface(const sp& mp, JNIEnv *env, jobject thiz) +{ + jobject surface = env->GetObjectField(thiz, fields.surface); + if (surface != NULL) { + const sp& native_surface = get_surface(env, surface); + LOGV("prepare: surface=%p (id=%d)", + native_surface.get(), native_surface->ID()); + mp->setVideoSurface(native_surface); + } +} + +static void +android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz) +{ + sp mp = getMediaPlayer(env, thiz); + if (mp == NULL ) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + setVideoSurface(mp, env, thiz); +} static void android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) @@ -207,13 +228,7 @@ android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) jniThrowException(env, "java/lang/IllegalStateException", NULL); return; } - jobject surface = env->GetObjectField(thiz, fields.surface); - if (surface != NULL) { - const sp& native_surface = get_surface(env, surface); - LOGV("prepare: surface=%p (id=%d)", - native_surface.get(), native_surface->ID()); - mp->setVideoSurface(native_surface); - } + setVideoSurface(mp, env, thiz); process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); } @@ -469,6 +484,7 @@ android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz) static JNINativeMethod gMethods[] = { {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource}, {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD}, + {"_setVideoSurface", "()V", (void *)android_media_MediaPlayer_setVideoSurface}, {"prepare", "()V", (void *)android_media_MediaPlayer_prepare}, {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync}, {"_start", "()V", (void *)android_media_MediaPlayer_start}, From d1c67d42abdf41d10c9a7589da1e0088af1e123a Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 29 May 2009 13:53:16 -0700 Subject: [PATCH 36/43] Fixes #1878499. Ignore touch up events that happen after a gesture was cancelled. This fix also improves performance by ignoring move events that are within the touch threshold. --- .../android/gesture/GestureOverlayView.java | 100 ++++++++++-------- core/java/android/gesture/GestureStroke.java | 2 +- core/res/res/values/styles.xml | 2 +- 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java index c21cc55c7c8b3..cc58f1d330ef3 100755 --- a/core/java/android/gesture/GestureOverlayView.java +++ b/core/java/android/gesture/GestureOverlayView.java @@ -75,7 +75,7 @@ public class GestureOverlayView extends FrameLayout { private int mInvalidateExtraBorder = 10; private int mGestureStrokeType = GESTURE_STROKE_TYPE_SINGLE; - private float mGestureStrokeLengthThreshold = 30.0f; + private float mGestureStrokeLengthThreshold = 50.0f; private float mGestureStrokeSquarenessTreshold = 0.275f; private float mGestureStrokeAngleThreshold = 40.0f; @@ -554,39 +554,39 @@ public class GestureOverlayView extends FrameLayout { mX = x; mY = y; - } - mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); + mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); - if (mHandleGestureActions && !mIsGesturing) { - mTotalLength += (float) Math.sqrt(dx * dx + dy * dy); + if (mHandleGestureActions && !mIsGesturing) { + mTotalLength += (float) Math.sqrt(dx * dx + dy * dy); - if (mTotalLength > mGestureStrokeLengthThreshold) { - final OrientedBoundingBox box = - GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer); + if (mTotalLength > mGestureStrokeLengthThreshold) { + final OrientedBoundingBox box = + GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer); - float angle = Math.abs(box.orientation); - if (angle > 90) { - angle = 180 - angle; - } + float angle = Math.abs(box.orientation); + if (angle > 90) { + angle = 180 - angle; + } - if (box.squareness > mGestureStrokeSquarenessTreshold || - (mOrientation == ORIENTATION_VERTICAL ? - angle < mGestureStrokeAngleThreshold : - angle > mGestureStrokeAngleThreshold)) { + if (box.squareness > mGestureStrokeSquarenessTreshold || + (mOrientation == ORIENTATION_VERTICAL ? + angle < mGestureStrokeAngleThreshold : + angle > mGestureStrokeAngleThreshold)) { - mIsGesturing = true; - setCurrentColor(mCertainGestureColor); + mIsGesturing = true; + setCurrentColor(mCertainGestureColor); + } } } - } - // pass the event to handlers - final ArrayList listeners = mOnGestureListeners; - final int count = listeners.size(); - for (int i = 0; i < count; i++) { - listeners.get(i).onGesture(this, event); - } + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGesture(this, event); + } + } return areaToRefresh; } @@ -594,35 +594,45 @@ public class GestureOverlayView extends FrameLayout { private void touchUp(MotionEvent event, boolean cancel) { mIsListeningForGestures = false; - // add the stroke to the current gesture - mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)); - mStrokeBuffer.clear(); + // A gesture wasn't started or was cancelled + if (mCurrentGesture != null) { + // add the stroke to the current gesture + mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer)); - if (!cancel) { - // pass the event to handlers - final ArrayList listeners = mOnGestureListeners; - int count = listeners.size(); - for (int i = 0; i < count; i++) { - listeners.get(i).onGestureEnded(this, event); - } + if (!cancel) { + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureEnded(this, event); + } + + if (mHandleGestureActions) { + clear(mFadeEnabled, mIsGesturing); + } + } else { + cancelGesture(event); - if (mHandleGestureActions) { - clear(mFadeEnabled, mIsGesturing); } } else { - // pass the event to handlers - final ArrayList listeners = mOnGestureListeners; - final int count = listeners.size(); - for (int i = 0; i < count; i++) { - listeners.get(i).onGestureCancelled(this, event); - } - - clear(false); + cancelGesture(event); } + mStrokeBuffer.clear(); mIsGesturing = false; } + private void cancelGesture(MotionEvent event) { + // pass the event to handlers + final ArrayList listeners = mOnGestureListeners; + final int count = listeners.size(); + for (int i = 0; i < count; i++) { + listeners.get(i).onGestureCancelled(this, event); + } + + clear(false); + } + private void fireOnGesturePerformed() { final ArrayList actionListeners = mOnGesturePerformedListeners; diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java index 90faaed6430f4..598eb8534ffc5 100644 --- a/core/java/android/gesture/GestureStroke.java +++ b/core/java/android/gesture/GestureStroke.java @@ -30,7 +30,7 @@ import java.util.ArrayList; * A gesture stroke started on a touch down and ended on a touch up. */ public class GestureStroke { - static final float TOUCH_TOLERANCE = 3; + static final float TOUCH_TOLERANCE = 8; public final RectF boundingBox; diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 490abde075d45..72402d0a1f551 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -177,7 +177,7 @@ #48ffff00 420 150 - 30.0 + 50.0 0.275 40.0 true From 867641ece36e2fd17faaea79cf19506ab17177c6 Mon Sep 17 00:00:00 2001 From: jsh Date: Wed, 27 May 2009 17:32:50 -0700 Subject: [PATCH 37/43] Telephony support for SMS memory reporting to the network. - Use ordered broadcast to allow receivers to set a result code. - Ack SMS with result code. - New RIL command to report memory status. - Fixed a typo in a Gservices setting. - Merge in CL 137895 (hold a wake lock while broadcasting SMS_RECEIVED). --- core/java/android/provider/Settings.java | 4 +- core/java/android/provider/Telephony.java | 31 +++++++- .../internal/telephony/CommandsInterface.java | 18 ++++- .../com/android/internal/telephony/RIL.java | 30 +++++-- .../internal/telephony/RILConstants.java | 1 + .../internal/telephony/SMSDispatcher.java | 79 ++++++++++++++++--- .../internal/telephony/WapPushOverSms.java | 27 ++----- .../telephony/cdma/CdmaSMSDispatcher.java | 20 ++++- .../telephony/gsm/GsmSMSDispatcher.java | 21 ++++- .../telephony/test/SimulatedCommands.java | 8 +- 10 files changed, 186 insertions(+), 53 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 559f224d12631..5d106756ad792 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2838,12 +2838,12 @@ public final class Settings { * out without asking for use permit, to limit the un-authorized SMS * usage. */ - public static final String SMS_OUTGOING_CEHCK_INTERVAL_MS = + public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = "sms_outgoing_check_interval_ms"; /** * The number of outgoing SMS sent without asking for user permit - * (of {@link #SMS_OUTGOING_CEHCK_INTERVAL_MS} + * (of {@link #SMS_OUTGOING_CHECK_INTERVAL_MS} */ public static final String SMS_OUTGOING_CEHCK_MAX_COUNT = "sms_outgoing_check_max_count"; diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index a4145c487696b..4078fa6d5a734 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -465,6 +465,24 @@ public final class Telephony { * Contains info about SMS related Intents that are broadcast. */ public static final class Intents { + /** + * Set by BroadcastReceiver. Indicates the message was handled + * successfully. + */ + public static final int RESULT_SMS_HANDLED = 1; + + /** + * Set by BroadcastReceiver. Indicates a generic error while + * processing the message. + */ + public static final int RESULT_SMS_GENERIC_ERROR = 2; + + /** + * Set by BroadcastReceiver. Indicates insufficient memory to store + * the message. + */ + public static final int RESULT_SMS_OUT_OF_MEMORY = 3; + /** * Broadcast Action: A new text based SMS message has been received * by the device. The intent will have the following extra @@ -476,7 +494,10 @@ public final class Telephony { *

* *

The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}

+ * {@link #getMessagesFromIntent(Intent)}.

+ * + *

If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

*/ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String SMS_RECEIVED_ACTION = @@ -493,7 +514,10 @@ public final class Telephony { * * *

The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}

+ * {@link #getMessagesFromIntent(Intent)}.

+ * + *

If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

*/ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String DATA_SMS_RECEIVED_ACTION = @@ -510,6 +534,9 @@ public final class Telephony { *
  • pduType (Integer) - The WAP PDU type
  • *
  • data - The data payload of the message
  • * + * + *

    If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

    */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String WAP_PUSH_RECEIVED_ACTION = diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index 94a1c13ab6bb8..34a57a026d95a 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -147,6 +147,14 @@ public interface CommandsInterface { static final int SIM_REFRESH_INIT = 1; // SIM initialized; reload all static final int SIM_REFRESH_RESET = 2; // SIM reset; may be locked + // GSM SMS fail cause for acknowledgeLastIncomingSMS. From TS 23.040, 9.2.3.22. + static final int GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED = 0xD3; + static final int GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR = 0xFF; + + // CDMA SMS fail cause for acknowledgeLastIncomingCdmaSms. From TS N.S00005, 6.5.2.125. + static final int CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE = 35; + static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM = 39; + //***** Methods RadioState getRadioState(); @@ -882,9 +890,9 @@ public interface CommandsInterface { void setRadioPower(boolean on, Message response); - void acknowledgeLastIncomingSMS(boolean success, Message response); + void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message response); - void acknowledgeLastIncomingCdmaSms(boolean success, Message response); + void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response); /** * parameters equivilient to 27.007 AT+CRSM command @@ -1087,6 +1095,12 @@ public interface CommandsInterface { */ void setSmscAddress(String address, Message result); + /** + * Indicates whether there is storage available for new SMS messages. + * @param available true if storage is available + * @param result callback message + */ + void reportSmsMemoryStatus(boolean available, Message result); void invokeOemRilRequestRaw(byte[] data, Message response); diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 792e67fda7b5e..ea84b09801e45 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -1339,28 +1339,32 @@ public final class RIL extends BaseCommands implements CommandsInterface { } public void - acknowledgeLastIncomingSMS(boolean success, Message result) { + acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_SMS_ACKNOWLEDGE, result); - rr.mp.writeInt(1); + rr.mp.writeInt(2); rr.mp.writeInt(success ? 1 : 0); + rr.mp.writeInt(cause); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + success + " " + cause); send(rr); } public void - acknowledgeLastIncomingCdmaSms(boolean success, Message result) { + acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result); + rr.mp.writeInt(2); rr.mp.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass // cause code according to X.S004-550E - rr.mp.writeInt(39); //39 means other terminal problem; is not interpreted for success. + rr.mp.writeInt(cause); - if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " " + success + " " + cause); send(rr); } @@ -1812,6 +1816,20 @@ public final class RIL extends BaseCommands implements CommandsInterface { send(rr); } + /** + * {@inheritDoc} + */ + public void reportSmsMemoryStatus(boolean available, Message result) { + RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result); + rr.mp.writeInt(1); + rr.mp.writeInt(available ? 1 : 0); + + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + + requestToString(rr.mRequest) + ": " + available); + + send(rr); + } + //***** Private Methods private void sendScreenState(boolean on) { diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 44c863b90aead..26995ef240620 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -222,6 +222,7 @@ cat include/telephony/ril.h | \ int RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE = 99; int RIL_REQUEST_GET_SMSC_ADDRESS = 100; int RIL_REQUEST_SET_SMSC_ADDRESS = 101; + int RIL_REQUEST_REPORT_SMS_MEMORY_STATUS = 102; int RIL_UNSOL_RESPONSE_BASE = 1000; int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000; int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001; diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index d055c3114f1ff..12808ceacd364 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.PendingIntent; import android.app.AlertDialog; import android.app.PendingIntent.CanceledException; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -32,6 +33,7 @@ import android.net.Uri; import android.os.AsyncResult; import android.os.Handler; import android.os.Message; +import android.os.PowerManager; import android.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.provider.Settings; @@ -128,6 +130,15 @@ public abstract class SMSDispatcher extends Handler { private SmsTracker mSTracker; + /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ + private PowerManager.WakeLock mWakeLock; + + /** + * Hold the wake lock for 5 seconds, which should be enough time for + * any receiver(s) to grab its own wake lock. + */ + private final int WAKE_LOCK_TIMEOUT = 5000; + private static SmsMessage mSmsMessage; private static SmsMessageBase mSmsMessageBase; private SmsMessageBase.SubmitPduBase mSubmitPduBase; @@ -196,14 +207,16 @@ public abstract class SMSDispatcher extends Handler { protected SMSDispatcher(PhoneBase phone) { mPhone = phone; - mWapPush = new WapPushOverSms(phone); + mWapPush = new WapPushOverSms(phone, this); mContext = phone.getContext(); mResolver = mContext.getContentResolver(); mCm = phone.mCM; mSTracker = null; + createWakelock(); + int check_period = Settings.Gservices.getInt(mResolver, - Settings.Gservices.SMS_OUTGOING_CEHCK_INTERVAL_MS, + Settings.Gservices.SMS_OUTGOING_CHECK_INTERVAL_MS, DEFAULT_SMS_CHECK_PERIOD); int max_count = Settings.Gservices.getInt(mResolver, Settings.Gservices.SMS_OUTGOING_CEHCK_MAX_COUNT, @@ -257,16 +270,17 @@ public abstract class SMSDispatcher extends Handler { ar = (AsyncResult) msg.obj; - // FIXME only acknowledge on store - acknowledgeLastIncomingSms(true, null); - if (ar.exception != null) { Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); return; } sms = (SmsMessage) ar.result; - dispatchMessage(sms.mWrappedSmsMessage); + try { + dispatchMessage(sms.mWrappedSmsMessage); + } catch (RuntimeException ex) { + acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); + } break; @@ -310,6 +324,28 @@ public abstract class SMSDispatcher extends Handler { } } + private void createWakelock() { + PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher"); + mWakeLock.setReferenceCounted(true); + } + + /** + * Grabs a wake lock and sends intent as an ordered broadcast. + * The resultReceiver will check for errors and ACK/NACK back + * to the RIL. + * + * @param intent intent to broadcast + * @param permission Receivers are required to have this permission + */ + void dispatch(Intent intent, String permission) { + // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any + // receivers time to take their own wake locks. + mWakeLock.acquire(WAKE_LOCK_TIMEOUT); + mContext.sendOrderedBroadcast(intent, permission, mResultReceiver, + this, Activity.RESULT_OK, null, null); + } + /** * Called when SIM_FULL message is received from the RIL. Notifies interested * parties that SIM storage for SMS messages is full. @@ -317,7 +353,8 @@ public abstract class SMSDispatcher extends Handler { private void handleIccFull(){ // broadcast SIM_FULL intent Intent intent = new Intent(Intents.SIM_FULL_ACTION); - mPhone.getContext().sendBroadcast(intent, "android.permission.RECEIVE_SMS"); + mWakeLock.acquire(WAKE_LOCK_TIMEOUT); + mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); } /** @@ -454,6 +491,7 @@ public abstract class SMSDispatcher extends Handler { values.put("destination_port", portAddrs.destPort); } mResolver.insert(mRawUri, values); + acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null); return; } @@ -475,7 +513,9 @@ public abstract class SMSDispatcher extends Handler { mResolver.delete(mRawUri, where.toString(), whereArgs); } catch (SQLException e) { Log.e(TAG, "Can't access multipart SMS database", e); - return; // TODO: NACK the message or something, don't just discard. + // TODO: Would OUT_OF_MEMORY be more appropriate? + acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); + return; } finally { if (cursor != null) cursor.close(); } @@ -519,8 +559,7 @@ public abstract class SMSDispatcher extends Handler { protected void dispatchPdus(byte[][] pdus) { Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); intent.putExtra("pdus", pdus); - mPhone.getContext().sendBroadcast( - intent, "android.permission.RECEIVE_SMS"); + dispatch(intent, "android.permission.RECEIVE_SMS"); } /** @@ -533,8 +572,7 @@ public abstract class SMSDispatcher extends Handler { Uri uri = Uri.parse("sms://localhost:" + port); Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); intent.putExtra("pdus", pdus); - mPhone.getContext().sendBroadcast( - intent, "android.permission.RECEIVE_SMS"); + dispatch(intent, "android.permission.RECEIVE_SMS"); } @@ -698,9 +736,11 @@ public abstract class SMSDispatcher extends Handler { /** * Send an acknowledge message. * @param success indicates that last message was successfully received. + * @param result result code indicating any error * @param response callback message sent when operation completes. */ - protected abstract void acknowledgeLastIncomingSms(boolean success, Message response); + protected abstract void acknowledgeLastIncomingSms(boolean success, + int result, Message response); /** * Check if a SmsTracker holds multi-part Sms @@ -751,4 +791,17 @@ public abstract class SMSDispatcher extends Handler { } } }; + + private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int rc = getResultCode(); + boolean success = (rc == Activity.RESULT_OK) || (rc == Intents.RESULT_SMS_HANDLED); + + // For a multi-part message, this only ACKs the last part. + // Previous parts were ACK'd as they were received. + acknowledgeLastIncomingSms(success, rc, null); + } + + }; } diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java index 98899c903684e..c851bc1b0fde3 100644 --- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -18,7 +18,6 @@ package com.android.internal.telephony; import android.content.Context; import android.content.Intent; -import android.os.PowerManager; import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; @@ -34,7 +33,7 @@ public class WapPushOverSms { private final Context mContext; private WspTypeDecoder pduDecoder; - private PowerManager.WakeLock mWakeLock; + private SMSDispatcher mSmsDispatcher; /** * Hold the wake lock for 5 seconds, which should be enough time for @@ -42,10 +41,9 @@ public class WapPushOverSms { */ private final int WAKE_LOCK_TIMEOUT = 5000; - public WapPushOverSms(Phone phone) { - + public WapPushOverSms(Phone phone, SMSDispatcher smsDispatcher) { + mSmsDispatcher = smsDispatcher; mContext = phone.getContext(); - createWakelock(); } /** @@ -184,7 +182,7 @@ public class WapPushOverSms { intent.putExtra("pduType", pduType); intent.putExtra("data", data); - sendBroadcast(intent, "android.permission.RECEIVE_WAP_PUSH"); + mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); } private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType) { @@ -194,7 +192,7 @@ public class WapPushOverSms { intent.putExtra("pduType", pduType); intent.putExtra("data", pdu); - sendBroadcast(intent, "android.permission.RECEIVE_WAP_PUSH"); + mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); } private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType, int dataIndex) { @@ -209,20 +207,7 @@ public class WapPushOverSms { intent.putExtra("pduType", pduType); intent.putExtra("data", data); - sendBroadcast(intent, "android.permission.RECEIVE_MMS"); - } - - private void createWakelock() { - PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WapPushOverSms"); - mWakeLock.setReferenceCounted(true); - } - - private void sendBroadcast(Intent intent, String permission) { - // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any - // receivers time to take their own wake locks. - mWakeLock.acquire(WAKE_LOCK_TIMEOUT); - mContext.sendBroadcast(intent, permission); + mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS"); } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index a3d00d73db026..f12e7e399e3aa 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -17,15 +17,18 @@ package com.android.internal.telephony.cdma; +import android.app.Activity; import android.app.PendingIntent; import android.content.ContentValues; import android.database.Cursor; import android.database.SQLException; import android.os.AsyncResult; import android.os.Message; +import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; +import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SMSDispatcher; @@ -330,10 +333,10 @@ final class CdmaSMSDispatcher extends SMSDispatcher { } /** {@inheritDoc} */ - protected void acknowledgeLastIncomingSms(boolean success, Message response){ + protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ // FIXME unit test leaves cm == null. this should change if (mCm != null) { - mCm.acknowledgeLastIncomingCdmaSms(success, response); + mCm.acknowledgeLastIncomingCdmaSms(success, resultToCause(result), response); } } @@ -352,4 +355,17 @@ final class CdmaSMSDispatcher extends SMSDispatcher { mCm.setCdmaBroadcastConfig(configValuesArray, response); } + private int resultToCause(int rc) { + switch (rc) { + case Activity.RESULT_OK: + case Intents.RESULT_SMS_HANDLED: + // Cause code is ignored on success. + return 0; + case Intents.RESULT_SMS_OUT_OF_MEMORY: + return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE; + case Intents.RESULT_SMS_GENERIC_ERROR: + default: + return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM; + } + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 699142a28aca8..347b3afc7a96c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -22,12 +22,14 @@ import android.app.PendingIntent.CanceledException; import android.content.Intent; import android.os.AsyncResult; import android.os.Message; +import android.provider.Telephony.Sms.Intents; import android.telephony.ServiceState; import android.util.Config; import android.util.Log; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.gsm.SmsMessage; +import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; @@ -78,7 +80,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { } if (mCm != null) { - mCm.acknowledgeLastIncomingSMS(true, null); + mCm.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null); } } @@ -284,10 +286,10 @@ final class GsmSMSDispatcher extends SMSDispatcher { } /** {@inheritDoc} */ - protected void acknowledgeLastIncomingSms(boolean success, Message response){ + protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ // FIXME unit test leaves cm == null. this should change if (mCm != null) { - mCm.acknowledgeLastIncomingSMS(success, response); + mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response); } } @@ -312,4 +314,17 @@ final class GsmSMSDispatcher extends SMSDispatcher { response.recycle(); } + private int resultToCause(int rc) { + switch (rc) { + case Activity.RESULT_OK: + case Intents.RESULT_SMS_HANDLED: + // Cause code is ignored on success. + return 0; + case Intents.RESULT_SMS_OUT_OF_MEMORY: + return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED; + case Intents.RESULT_SMS_GENERIC_ERROR: + default: + return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR; + } + } } diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java index 19679aa156020..9fb9be82b4602 100644 --- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java +++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java @@ -1018,6 +1018,10 @@ public final class SimulatedCommands extends BaseCommands unimplemented(result); } + public void reportSmsMemoryStatus(boolean available, Message result) { + unimplemented(result); + } + private boolean isSimLocked() { if (mSimLockedState != SimLockState.NONE) { return true; @@ -1041,11 +1045,11 @@ public final class SimulatedCommands extends BaseCommands } - public void acknowledgeLastIncomingSMS(boolean success, Message result) { + public void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) { unimplemented(result); } - public void acknowledgeLastIncomingCdmaSms(boolean success, Message result) { + public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) { unimplemented(result); } From e1ab4349268f3190f8ae6b535c4583e5f23f0a59 Mon Sep 17 00:00:00 2001 From: Jack Palevich Date: Fri, 29 May 2009 11:38:46 -0700 Subject: [PATCH 38/43] Don't prelink the libRS libraries. These are temporary libraries for the development of RenderScript, so they don't have to be prelinked. --- libs/rs/Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 979c4418590ed..6f7280225bdfa 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -97,6 +97,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui libacc LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libRS +LOCAL_PRELINK_MODULE := false #LOCAL_MODULE_TAGS := tests @@ -131,6 +132,7 @@ LOCAL_CFLAGS += LOCAL_LDLIBS := -lpthread LOCAL_MODULE:= libRS_jni +LOCAL_PRELINK_MODULE := false LOCAL_ADDITIONAL_DEPENDENCIES += $(RS_GENERATED_SOURCES) From 0a63716ed0e44f7cd32b81a444429318d42d8f08 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 29 May 2009 14:43:54 -0700 Subject: [PATCH 39/43] Modify the base gestures API to use streams instead of files. Adds new wrappers to easily load/save gestures from files, resources, etc. Do the same for the letters recognizer. --- .../android/gesture/GestureLibraries.java | 139 ++++++++ core/java/android/gesture/GestureLibrary.java | 327 ++--------------- core/java/android/gesture/GestureStore.java | 330 ++++++++++++++++++ core/java/android/gesture/Instance.java | 4 +- .../java/android/gesture/InstanceLearner.java | 2 +- .../android/gesture/LetterRecognizer.java | 53 ++- .../android/gesture/LetterRecognizers.java | 65 ++++ core/java/android/widget/AbsListView.java | 77 ++-- .../example/ContactListGestureOverlay.java | 5 +- .../android/gesture/example/GestureEntry.java | 23 +- .../gesture/example/GestureLibViewer.java | 31 +- 11 files changed, 660 insertions(+), 396 deletions(-) create mode 100644 core/java/android/gesture/GestureLibraries.java create mode 100644 core/java/android/gesture/GestureStore.java create mode 100644 core/java/android/gesture/LetterRecognizers.java diff --git a/core/java/android/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java new file mode 100644 index 0000000000000..2ce7a8e57ad28 --- /dev/null +++ b/core/java/android/gesture/GestureLibraries.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gesture; + +import android.util.Log; +import static android.gesture.GestureConstants.*; +import android.content.Context; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.ref.WeakReference; + +public final class GestureLibraries { + private GestureLibraries() { + } + + public static GestureLibrary fromFile(String path) { + return fromFile(new File(path)); + } + + public static GestureLibrary fromFile(File path) { + return new FileGestureLibrary(path); + } + + public static GestureLibrary fromPrivateFile(Context context, String name) { + return fromFile(context.getFileStreamPath(name)); + } + + public static GestureLibrary fromRawResource(Context context, int resourceId) { + return new ResourceGestureLibrary(context, resourceId); + } + + private static class FileGestureLibrary extends GestureLibrary { + private final File mPath; + + public FileGestureLibrary(File path) { + mPath = path; + } + + @Override + public boolean isReadOnly() { + return !mPath.canWrite(); + } + + public boolean save() { + final File file = mPath; + if (!file.canWrite()) return false; + + if (!file.getParentFile().exists()) { + if (!file.getParentFile().mkdirs()) { + return false; + } + } + + boolean result = false; + try { + mStore.save(new FileOutputStream(file), true); + result = true; + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e); + } catch (IOException e) { + Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e); + } + + return result; + } + + public boolean load() { + boolean result = false; + final File file = mPath; + if (file.exists() && file.canRead()) { + try { + mStore.load(new FileInputStream(file), true); + result = true; + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e); + } catch (IOException e) { + Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e); + } + } + + return result; + } + } + + private static class ResourceGestureLibrary extends GestureLibrary { + private final WeakReference mContext; + private final int mResourceId; + + public ResourceGestureLibrary(Context context, int resourceId) { + mContext = new WeakReference(context); + mResourceId = resourceId; + } + + @Override + public boolean isReadOnly() { + return true; + } + + public boolean save() { + return false; + } + + public boolean load() { + boolean result = false; + final Context context = mContext.get(); + if (context != null) { + final InputStream in = context.getResources().openRawResource(mResourceId); + try { + mStore.load(in, true); + result = true; + } catch (IOException e) { + Log.d(LOG_TAG, "Could not load the gesture library from raw resource " + + context.getResources().getResourceName(mResourceId), e); + } + } + + return result; + } + } +} diff --git a/core/java/android/gesture/GestureLibrary.java b/core/java/android/gesture/GestureLibrary.java index 9020d2bafc056..a29c2c83cfbf0 100644 --- a/core/java/android/gesture/GestureLibrary.java +++ b/core/java/android/gesture/GestureLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 The Android Open Source Project + * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,337 +14,68 @@ * limitations under the License. */ + package android.gesture; -import android.util.Log; -import android.os.SystemClock; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.DataOutputStream; -import java.io.DataInputStream; -import java.util.ArrayList; -import java.util.HashMap; import java.util.Set; -import java.util.Map; +import java.util.ArrayList; -import static android.gesture.GestureConstants.LOG_TAG; +public abstract class GestureLibrary { + protected final GestureStore mStore; -/** - * GestureLibrary maintains gesture examples and makes predictions on a new - * gesture - */ -// -// File format for GestureLibrary: -// -// Nb. bytes Java type Description -// ----------------------------------- -// Header -// 2 bytes short File format version number -// 4 bytes int Number of entries -// Entry -// X bytes UTF String Entry name -// 4 bytes int Number of gestures -// Gesture -// 8 bytes long Gesture ID -// 4 bytes int Number of strokes -// Stroke -// 4 bytes int Number of points -// Point -// 4 bytes float X coordinate of the point -// 4 bytes float Y coordinate of the point -// 8 bytes long Time stamp -// -public class GestureLibrary { - public static final int SEQUENCE_INVARIANT = 1; - // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed - public static final int SEQUENCE_SENSITIVE = 2; - - // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures - public static final int ORIENTATION_INVARIANT = 1; - public static final int ORIENTATION_SENSITIVE = 2; - - private static final short FILE_FORMAT_VERSION = 1; - - private static final boolean PROFILE_LOADING_SAVING = false; - - private int mSequenceType = SEQUENCE_SENSITIVE; - private int mOrientationStyle = ORIENTATION_SENSITIVE; - - private final String mGestureFileName; - - private final HashMap> mNamedGestures = - new HashMap>(); - - private Learner mClassifier; - - private boolean mChanged = false; - - /** - * @param path where gesture data is stored - */ - public GestureLibrary(String path) { - mGestureFileName = path; - mClassifier = new InstanceLearner(); + protected GestureLibrary() { + mStore = new GestureStore(); + } + + public abstract boolean save(); + + public abstract boolean load(); + + public boolean isReadOnly() { + return false; + } + + public Learner getLearner() { + return mStore.getLearner(); } - /** - * Specify how the gesture library will handle orientation. - * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE - * - * @param style - */ public void setOrientationStyle(int style) { - mOrientationStyle = style; + mStore.setOrientationStyle(style); } public int getOrientationStyle() { - return mOrientationStyle; + return mStore.getOrientationStyle(); } - /** - * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE - */ public void setSequenceType(int type) { - mSequenceType = type; + mStore.setSequenceType(type); } - /** - * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE - */ public int getSequenceType() { - return mSequenceType; + return mStore.getSequenceType(); } - /** - * Get all the gesture entry names in the library - * - * @return a set of strings - */ public Set getGestureEntries() { - return mNamedGestures.keySet(); + return mStore.getGestureEntries(); } - /** - * Recognize a gesture - * - * @param gesture the query - * @return a list of predictions of possible entries for a given gesture - */ public ArrayList recognize(Gesture gesture) { - Instance instance = Instance.createInstance(mSequenceType, mOrientationStyle, gesture, null); - return mClassifier.classify(mSequenceType, instance.vector); + return mStore.recognize(gesture); } - /** - * Add a gesture for the entry - * - * @param entryName entry name - * @param gesture - */ public void addGesture(String entryName, Gesture gesture) { - if (entryName == null || entryName.length() == 0) { - return; - } - ArrayList gestures = mNamedGestures.get(entryName); - if (gestures == null) { - gestures = new ArrayList(); - mNamedGestures.put(entryName, gestures); - } - gestures.add(gesture); - mClassifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName)); - mChanged = true; + mStore.addGesture(entryName, gesture); } - /** - * Remove a gesture from the library. If there are no more gestures for the - * given entry, the gesture entry will be removed. - * - * @param entryName entry name - * @param gesture - */ public void removeGesture(String entryName, Gesture gesture) { - ArrayList gestures = mNamedGestures.get(entryName); - if (gestures == null) { - return; - } - - gestures.remove(gesture); - - // if there are no more samples, remove the entry automatically - if (gestures.isEmpty()) { - mNamedGestures.remove(entryName); - } - - mClassifier.removeInstance(gesture.getID()); - - mChanged = true; + mStore.removeGesture(entryName, gesture); } - /** - * Remove a entry of gestures - * - * @param entryName the entry name - */ public void removeEntry(String entryName) { - mNamedGestures.remove(entryName); - mClassifier.removeInstances(entryName); - mChanged = true; + mStore.removeEntry(entryName); } - /** - * Get all the gestures of an entry - * - * @param entryName - * @return the list of gestures that is under this name - */ public ArrayList getGestures(String entryName) { - ArrayList gestures = mNamedGestures.get(entryName); - if (gestures != null) { - return new ArrayList(gestures); - } else { - return null; - } - } - - /** - * Save the gesture library - */ - public boolean save() { - if (!mChanged) { - return true; - } - - boolean result = false; - DataOutputStream out = null; - - try { - File file = new File(mGestureFileName); - if (!file.getParentFile().exists()) { - if (!file.getParentFile().mkdirs()) { - return false; - } - } - - long start; - if (PROFILE_LOADING_SAVING) { - start = SystemClock.elapsedRealtime(); - } - - final HashMap> maps = mNamedGestures; - - out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file), - GestureConstants.IO_BUFFER_SIZE)); - // Write version number - out.writeShort(FILE_FORMAT_VERSION); - // Write number of entries - out.writeInt(maps.size()); - - for (Map.Entry> entry : maps.entrySet()) { - final String key = entry.getKey(); - final ArrayList examples = entry.getValue(); - final int count = examples.size(); - - // Write entry name - out.writeUTF(key); - // Write number of examples for this entry - out.writeInt(count); - - for (int i = 0; i < count; i++) { - examples.get(i).serialize(out); - } - } - - out.flush(); - - if (PROFILE_LOADING_SAVING) { - long end = SystemClock.elapsedRealtime(); - Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms"); - } - - mChanged = false; - result = true; - } catch (IOException ex) { - Log.d(LOG_TAG, "Failed to save gestures:", ex); - } finally { - GestureUtilities.closeStream(out); - } - - return result; - } - - /** - * Load the gesture library - */ - public boolean load() { - boolean result = false; - - final File file = new File(mGestureFileName); - if (file.exists()) { - DataInputStream in = null; - try { - in = new DataInputStream(new BufferedInputStream( - new FileInputStream(mGestureFileName), GestureConstants.IO_BUFFER_SIZE)); - - long start; - if (PROFILE_LOADING_SAVING) { - start = SystemClock.elapsedRealtime(); - } - - // Read file format version number - final short versionNumber = in.readShort(); - switch (versionNumber) { - case 1: - readFormatV1(in); - break; - } - - if (PROFILE_LOADING_SAVING) { - long end = SystemClock.elapsedRealtime(); - Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms"); - } - - result = true; - } catch (IOException ex) { - Log.d(LOG_TAG, "Failed to load gestures:", ex); - } finally { - GestureUtilities.closeStream(in); - } - } - - return result; - } - - private void readFormatV1(DataInputStream in) throws IOException { - final Learner classifier = mClassifier; - final HashMap> namedGestures = mNamedGestures; - namedGestures.clear(); - - // Number of entries in the library - final int entriesCount = in.readInt(); - - for (int i = 0; i < entriesCount; i++) { - // Entry name - final String name = in.readUTF(); - // Number of gestures - final int gestureCount = in.readInt(); - - final ArrayList gestures = new ArrayList(gestureCount); - for (int j = 0; j < gestureCount; j++) { - final Gesture gesture = Gesture.deserialize(in); - gestures.add(gesture); - classifier.addInstance(Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name)); - } - - namedGestures.put(name, gestures); - } - } - - Learner getLearner() { - return mClassifier; + return mStore.getGestures(entryName); } } diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java new file mode 100644 index 0000000000000..ddf1c8330b171 --- /dev/null +++ b/core/java/android/gesture/GestureStore.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2008-2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gesture; + +import android.util.Log; +import android.os.SystemClock; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.DataOutputStream; +import java.io.DataInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; +import java.util.Map; + +import static android.gesture.GestureConstants.LOG_TAG; + +/** + * GestureLibrary maintains gesture examples and makes predictions on a new + * gesture + */ +// +// File format for GestureStore: +// +// Nb. bytes Java type Description +// ----------------------------------- +// Header +// 2 bytes short File format version number +// 4 bytes int Number of entries +// Entry +// X bytes UTF String Entry name +// 4 bytes int Number of gestures +// Gesture +// 8 bytes long Gesture ID +// 4 bytes int Number of strokes +// Stroke +// 4 bytes int Number of points +// Point +// 4 bytes float X coordinate of the point +// 4 bytes float Y coordinate of the point +// 8 bytes long Time stamp +// +public class GestureStore { + public static final int SEQUENCE_INVARIANT = 1; + // when SEQUENCE_SENSITIVE is used, only single stroke gestures are currently allowed + public static final int SEQUENCE_SENSITIVE = 2; + + // ORIENTATION_SENSITIVE and ORIENTATION_INVARIANT are only for SEQUENCE_SENSITIVE gestures + public static final int ORIENTATION_INVARIANT = 1; + public static final int ORIENTATION_SENSITIVE = 2; + + private static final short FILE_FORMAT_VERSION = 1; + + private static final boolean PROFILE_LOADING_SAVING = false; + + private int mSequenceType = SEQUENCE_SENSITIVE; + private int mOrientationStyle = ORIENTATION_SENSITIVE; + + private final HashMap> mNamedGestures = + new HashMap>(); + + private Learner mClassifier; + + private boolean mChanged = false; + + public GestureStore() { + mClassifier = new InstanceLearner(); + } + + /** + * Specify how the gesture library will handle orientation. + * Use ORIENTATION_INVARIANT or ORIENTATION_SENSITIVE + * + * @param style + */ + public void setOrientationStyle(int style) { + mOrientationStyle = style; + } + + public int getOrientationStyle() { + return mOrientationStyle; + } + + /** + * @param type SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE + */ + public void setSequenceType(int type) { + mSequenceType = type; + } + + /** + * @return SEQUENCE_INVARIANT or SEQUENCE_SENSITIVE + */ + public int getSequenceType() { + return mSequenceType; + } + + /** + * Get all the gesture entry names in the library + * + * @return a set of strings + */ + public Set getGestureEntries() { + return mNamedGestures.keySet(); + } + + /** + * Recognize a gesture + * + * @param gesture the query + * @return a list of predictions of possible entries for a given gesture + */ + public ArrayList recognize(Gesture gesture) { + Instance instance = Instance.createInstance(mSequenceType, + mOrientationStyle, gesture, null); + return mClassifier.classify(mSequenceType, instance.vector); + } + + /** + * Add a gesture for the entry + * + * @param entryName entry name + * @param gesture + */ + public void addGesture(String entryName, Gesture gesture) { + if (entryName == null || entryName.length() == 0) { + return; + } + ArrayList gestures = mNamedGestures.get(entryName); + if (gestures == null) { + gestures = new ArrayList(); + mNamedGestures.put(entryName, gestures); + } + gestures.add(gesture); + mClassifier.addInstance( + Instance.createInstance(mSequenceType, mOrientationStyle, gesture, entryName)); + mChanged = true; + } + + /** + * Remove a gesture from the library. If there are no more gestures for the + * given entry, the gesture entry will be removed. + * + * @param entryName entry name + * @param gesture + */ + public void removeGesture(String entryName, Gesture gesture) { + ArrayList gestures = mNamedGestures.get(entryName); + if (gestures == null) { + return; + } + + gestures.remove(gesture); + + // if there are no more samples, remove the entry automatically + if (gestures.isEmpty()) { + mNamedGestures.remove(entryName); + } + + mClassifier.removeInstance(gesture.getID()); + + mChanged = true; + } + + /** + * Remove a entry of gestures + * + * @param entryName the entry name + */ + public void removeEntry(String entryName) { + mNamedGestures.remove(entryName); + mClassifier.removeInstances(entryName); + mChanged = true; + } + + /** + * Get all the gestures of an entry + * + * @param entryName + * @return the list of gestures that is under this name + */ + public ArrayList getGestures(String entryName) { + ArrayList gestures = mNamedGestures.get(entryName); + if (gestures != null) { + return new ArrayList(gestures); + } else { + return null; + } + } + + /** + * Save the gesture library + */ + public void save(OutputStream stream) throws IOException { + save(stream, false); + } + + public void save(OutputStream stream, boolean closeStream) throws IOException { + if (!mChanged) { + return; + } + + DataOutputStream out = null; + + try { + long start; + if (PROFILE_LOADING_SAVING) { + start = SystemClock.elapsedRealtime(); + } + + final HashMap> maps = mNamedGestures; + + out = new DataOutputStream((stream instanceof BufferedOutputStream) ? out : + new BufferedOutputStream(out, GestureConstants.IO_BUFFER_SIZE)); + // Write version number + out.writeShort(FILE_FORMAT_VERSION); + // Write number of entries + out.writeInt(maps.size()); + + for (Map.Entry> entry : maps.entrySet()) { + final String key = entry.getKey(); + final ArrayList examples = entry.getValue(); + final int count = examples.size(); + + // Write entry name + out.writeUTF(key); + // Write number of examples for this entry + out.writeInt(count); + + for (int i = 0; i < count; i++) { + examples.get(i).serialize(out); + } + } + + out.flush(); + + if (PROFILE_LOADING_SAVING) { + long end = SystemClock.elapsedRealtime(); + Log.d(LOG_TAG, "Saving gestures library = " + (end - start) + " ms"); + } + + mChanged = false; + } finally { + if (closeStream) GestureUtilities.closeStream(out); + } + } + + /** + * Load the gesture library + */ + public void load(InputStream stream) throws IOException { + load(stream, false); + } + + public void load(InputStream stream, boolean closeStream) throws IOException { + DataInputStream in = null; + try { + in = new DataInputStream((stream instanceof BufferedInputStream) ? stream : + new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE)); + + long start; + if (PROFILE_LOADING_SAVING) { + start = SystemClock.elapsedRealtime(); + } + + // Read file format version number + final short versionNumber = in.readShort(); + switch (versionNumber) { + case 1: + readFormatV1(in); + break; + } + + if (PROFILE_LOADING_SAVING) { + long end = SystemClock.elapsedRealtime(); + Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms"); + } + } finally { + if (closeStream) GestureUtilities.closeStream(in); + } + } + + private void readFormatV1(DataInputStream in) throws IOException { + final Learner classifier = mClassifier; + final HashMap> namedGestures = mNamedGestures; + namedGestures.clear(); + + // Number of entries in the library + final int entriesCount = in.readInt(); + + for (int i = 0; i < entriesCount; i++) { + // Entry name + final String name = in.readUTF(); + // Number of gestures + final int gestureCount = in.readInt(); + + final ArrayList gestures = new ArrayList(gestureCount); + for (int j = 0; j < gestureCount; j++) { + final Gesture gesture = Gesture.deserialize(in); + gestures.add(gesture); + classifier.addInstance( + Instance.createInstance(mSequenceType, mOrientationStyle, gesture, name)); + } + + namedGestures.put(name, gestures); + } + } + + Learner getLearner() { + return mClassifier; + } +} diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java index 9ac0a96be6064..ef208acf4ec79 100755 --- a/core/java/android/gesture/Instance.java +++ b/core/java/android/gesture/Instance.java @@ -72,7 +72,7 @@ class Instance { static Instance createInstance(int sequenceType, int orientationType, Gesture gesture, String label) { float[] pts; Instance instance; - if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) { + if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { pts = temporalSampler(orientationType, gesture); instance = new Instance(gesture.getID(), pts, label); instance.normalize(); @@ -94,7 +94,7 @@ class Instance { float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]); float adjustment = -orientation; - if (orientationType == GestureLibrary.ORIENTATION_SENSITIVE) { + if (orientationType == GestureStore.ORIENTATION_SENSITIVE) { int count = ORIENTATIONS.length; for (int i = 0; i < count; i++) { float delta = ORIENTATIONS[i] - orientation; diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java index b6feb6477f298..00cdadce7cd2a 100644 --- a/core/java/android/gesture/InstanceLearner.java +++ b/core/java/android/gesture/InstanceLearner.java @@ -38,7 +38,7 @@ class InstanceLearner extends Learner { continue; } double distance; - if (sequenceType == GestureLibrary.SEQUENCE_SENSITIVE) { + if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) { distance = GestureUtilities.cosineDistance(sample.vector, vector); } else { distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector); diff --git a/core/java/android/gesture/LetterRecognizer.java b/core/java/android/gesture/LetterRecognizer.java index 9e801ed435629..580fc26191f75 100644 --- a/core/java/android/gesture/LetterRecognizer.java +++ b/core/java/android/gesture/LetterRecognizer.java @@ -23,6 +23,7 @@ import android.util.Log; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -31,10 +32,9 @@ import java.util.HashMap; import static android.gesture.GestureConstants.LOG_TAG; public class LetterRecognizer { - public final static int RECOGNIZER_LATIN_LOWERCASE = 0; static final String GESTURE_FILE_NAME = "letters.gestures"; - private final static int ADJUST_RANGE = 3; + private final static int ADJUST_RANGE = 3; private SigmoidUnit[] mHiddenLayer; private SigmoidUnit[] mOutputLayer; @@ -42,8 +42,8 @@ public class LetterRecognizer { private final String[] mClasses; private final int mPatchSize; - - private GestureLibrary mGestureLibrary; + + private GestureLibrary mGestureStore; private final Comparator mComparator = new PredictionComparator(); @@ -69,15 +69,6 @@ public class LetterRecognizer { } } - public static LetterRecognizer getLetterRecognizer(Context context, int type) { - switch (type) { - case RECOGNIZER_LATIN_LOWERCASE: { - return createFromResource(context, com.android.internal.R.raw.latin_lowercase); - } - } - return null; - } - private LetterRecognizer(int numOfInput, int numOfHidden, String[] classes) { mPatchSize = (int) Math.sqrt(numOfInput); mHiddenLayer = new SigmoidUnit[numOfHidden]; @@ -137,14 +128,18 @@ public class LetterRecognizer { return output; } - private static LetterRecognizer createFromResource(Context context, int resourceID) { + static LetterRecognizer createFromResource(Context context, int resourceID) { final Resources resources = context.getResources(); + final InputStream stream = resources.openRawResource(resourceID); + return createFromStream(stream); + } + static LetterRecognizer createFromStream(InputStream stream) { DataInputStream in = null; LetterRecognizer classifier = null; try { - in = new DataInputStream(new BufferedInputStream(resources.openRawResource(resourceID), + in = new DataInputStream(new BufferedInputStream(stream, GestureConstants.IO_BUFFER_SIZE)); final int version = in.readShort(); @@ -206,49 +201,49 @@ public class LetterRecognizer { } /** - * TODO: Publish this API once we figure out where we should save the personzlied + * TODO: Publish this API once we figure out where we should save the personalized * gestures, and how to do so across all apps * * @hide */ public boolean save() { - if (mGestureLibrary != null) { - return mGestureLibrary.save(); + if (mGestureStore != null) { + return mGestureStore.save(); } return false; } /** - * TODO: Publish this API once we figure out where we should save the personzlied + * TODO: Publish this API once we figure out where we should save the personalized * gestures, and how to do so across all apps * * @hide */ public void setPersonalizationEnabled(boolean enabled) { if (enabled) { - mGestureLibrary = new GestureLibrary(GESTURE_FILE_NAME); - mGestureLibrary.setSequenceType(GestureLibrary.SEQUENCE_INVARIANT); - mGestureLibrary.load(); + mGestureStore = GestureLibraries.fromFile(GESTURE_FILE_NAME); + mGestureStore.setSequenceType(GestureStore.SEQUENCE_INVARIANT); + mGestureStore.load(); } else { - mGestureLibrary = null; + mGestureStore = null; } } /** - * TODO: Publish this API once we figure out where we should save the personzlied + * TODO: Publish this API once we figure out where we should save the personalized * gestures, and how to do so across all apps * * @hide */ public void addExample(String letter, Gesture example) { - if (mGestureLibrary != null) { - mGestureLibrary.addGesture(letter, example); + if (mGestureStore != null) { + mGestureStore.addGesture(letter, example); } } - + private void adjustPrediction(Gesture query, ArrayList predictions) { - if (mGestureLibrary != null) { - final ArrayList results = mGestureLibrary.recognize(query); + if (mGestureStore != null) { + final ArrayList results = mGestureStore.recognize(query); final HashMap topNList = new HashMap(); for (int j = 0; j < ADJUST_RANGE; j++) { diff --git a/core/java/android/gesture/LetterRecognizers.java b/core/java/android/gesture/LetterRecognizers.java new file mode 100644 index 0000000000000..e3f45a0308e13 --- /dev/null +++ b/core/java/android/gesture/LetterRecognizers.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.gesture; + +import android.content.Context; +import android.util.Log; +import static android.gesture.GestureConstants.LOG_TAG; +import static android.gesture.LetterRecognizer.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public final class LetterRecognizers { + public final static int RECOGNIZER_LATIN_LOWERCASE = 0; + + private LetterRecognizers() { + } + + public static LetterRecognizer fromType(Context context, int type) { + switch (type) { + case RECOGNIZER_LATIN_LOWERCASE: { + return createFromResource(context, com.android.internal.R.raw.latin_lowercase); + } + } + return null; + } + + public static LetterRecognizer fromResource(Context context, int resourceId) { + return createFromResource(context, resourceId); + } + + public static LetterRecognizer fromFile(String path) { + return fromFile(new File(path)); + } + + public static LetterRecognizer fromFile(File file) { + try { + return createFromStream(new FileInputStream(file)); + } catch (FileNotFoundException e) { + Log.d(LOG_TAG, "Failed to load handwriting data from file " + file, e); + } + return null; + } + + public static LetterRecognizer fromStream(InputStream stream) { + return createFromStream(stream); + } +} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index f60aba35883fa..ec021432ba59a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -52,6 +52,7 @@ import android.gesture.GestureOverlayView; import android.gesture.Gesture; import android.gesture.LetterRecognizer; import android.gesture.Prediction; +import android.gesture.LetterRecognizers; import com.android.internal.R; @@ -94,7 +95,7 @@ public abstract class AbsListView extends AdapterView implements Te public static final int TRANSCRIPT_MODE_NORMAL = 1; /** * The list will automatically scroll to the bottom, no matter what items - * are currently visible. + * are currently visible. * * @see #setTranscriptMode(int) */ @@ -156,7 +157,7 @@ public abstract class AbsListView extends AdapterView implements Te * Indicates the view is in the process of being flung */ static final int TOUCH_MODE_FLING = 4; - + /** * Indicates that the user is currently dragging the fast scroll thumb */ @@ -349,7 +350,7 @@ public abstract class AbsListView extends AdapterView implements Te * bitmap cache after scrolling. */ boolean mScrollingCacheEnabled; - + /** * Whether or not to enable the fast scroll feature on this list */ @@ -422,7 +423,7 @@ public abstract class AbsListView extends AdapterView implements Te * The last CheckForTap runnable we posted, if any */ private Runnable mPendingCheckForTap; - + /** * The last CheckForKeyLongPress runnable we posted, if any */ @@ -576,7 +577,7 @@ public abstract class AbsListView extends AdapterView implements Te int color = a.getColor(R.styleable.AbsListView_cacheColorHint, 0); setCacheColorHint(color); - + boolean enableFastScroll = a.getBoolean(R.styleable.AbsListView_fastScrollEnabled, false); setFastScrollEnabled(enableFastScroll); @@ -605,7 +606,7 @@ public abstract class AbsListView extends AdapterView implements Te mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); mDensityScale = getContext().getResources().getDisplayMetrics().density; } - + /** *

    Sets the type of gestures to use with this list. When gestures are enabled, * that is if the gestures parameter is not {@link #GESTURES_NONE}, @@ -663,7 +664,7 @@ public abstract class AbsListView extends AdapterView implements Te * @see #GESTURES_NONE * @see #GESTURES_JUMP * @see #GESTURES_FILTER - * @see #setGestures(int) + * @see #setGestures(int) */ @ViewDebug.ExportedProperty(mapping = { @ViewDebug.IntToString(from = GESTURES_NONE, to = "NONE"), @@ -737,7 +738,7 @@ public abstract class AbsListView extends AdapterView implements Te mGesturesOverlay.setGestureStrokeType(GestureOverlayView.GESTURE_STROKE_TYPE_MULTIPLE); mGesturesOverlay.addOnGesturePerformedListener(new GesturesProcessor()); - mPreviousGesturing = false; + mPreviousGesturing = false; } } @@ -778,10 +779,10 @@ public abstract class AbsListView extends AdapterView implements Te } /** - * Enables fast scrolling by letting the user quickly scroll through lists by - * dragging the fast scroll thumb. The adapter attached to the list may want + * Enables fast scrolling by letting the user quickly scroll through lists by + * dragging the fast scroll thumb. The adapter attached to the list may want * to implement {@link SectionIndexer} if it wishes to display alphabet preview and - * jump between sections of the list. + * jump between sections of the list. * @see SectionIndexer * @see #isFastScrollEnabled() * @param enabled whether or not to enable fast scrolling @@ -799,7 +800,7 @@ public abstract class AbsListView extends AdapterView implements Te } } } - + /** * Returns the current state of the fast scroll feature. * @see #setFastScrollEnabled(boolean) @@ -809,10 +810,10 @@ public abstract class AbsListView extends AdapterView implements Te public boolean isFastScrollEnabled() { return mFastScrollEnabled; } - + /** * If fast scroll is visible, then don't draw the vertical scrollbar. - * @hide + * @hide */ @Override protected boolean isVerticalScrollBarHidden() { @@ -830,11 +831,11 @@ public abstract class AbsListView extends AdapterView implements Te * When smooth scrollbar is disabled, the position and size of the scrollbar thumb * is based solely on the number of items in the adapter and the position of the * visible items inside the adapter. This provides a stable scrollbar as the user - * navigates through a list of items with varying heights. + * navigates through a list of items with varying heights. * * @param enabled Whether or not to enable smooth scrollbar. * - * @see #setSmoothScrollbarEnabled(boolean) + * @see #setSmoothScrollbarEnabled(boolean) * @attr ref android.R.styleable#AbsListView_smoothScrollbar */ public void setSmoothScrollbarEnabled(boolean enabled) { @@ -1142,7 +1143,7 @@ public abstract class AbsListView extends AdapterView implements Te /** * Sets the initial value for the text filter. * @param filterText The text to use for the filter. - * + * * @see #setTextFilterEnabled */ public void setFilterText(String filterText) { @@ -1168,7 +1169,7 @@ public abstract class AbsListView extends AdapterView implements Te } /** - * Returns the list's text filter, if available. + * Returns the list's text filter, if available. * @return the list's text filter or null if filtering isn't enabled */ public CharSequence getTextFilter() { @@ -1177,7 +1178,7 @@ public abstract class AbsListView extends AdapterView implements Te } return null; } - + @Override protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); @@ -1740,7 +1741,7 @@ public abstract class AbsListView extends AdapterView implements Te System.arraycopy(state, enabledPos + 1, state, enabledPos, state.length - enabledPos - 1); } - + return state; } @@ -1851,16 +1852,16 @@ public abstract class AbsListView extends AdapterView implements Te */ private class WindowRunnnable { private int mOriginalAttachCount; - + public void rememberWindowAttachCount() { mOriginalAttachCount = getWindowAttachCount(); } - + public boolean sameWindow() { return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount; } } - + private class PerformClick extends WindowRunnnable implements Runnable { View mChild; int mClickMotionPosition; @@ -1887,7 +1888,7 @@ public abstract class AbsListView extends AdapterView implements Te final long longPressId = mAdapter.getItemId(mMotionPosition); boolean handled = false; - if (sameWindow() && !mDataChanged) { + if (sameWindow() && !mDataChanged) { handled = performLongPress(child, longPressPosition, longPressId); } if (handled) { @@ -1901,7 +1902,7 @@ public abstract class AbsListView extends AdapterView implements Te } } } - + private class CheckForKeyLongPress extends WindowRunnnable implements Runnable { public void run() { if (isPressed() && mSelectedPosition >= 0) { @@ -1930,7 +1931,7 @@ public abstract class AbsListView extends AdapterView implements Te boolean handled = false; dismissGesturesPopup(); - + if (mOnItemLongClickListener != null) { handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child, longPressPosition, longPressId); @@ -2079,7 +2080,7 @@ public abstract class AbsListView extends AdapterView implements Te mTouchMode = TOUCH_MODE_DONE_WAITING; } } else { - mTouchMode = TOUCH_MODE_DONE_WAITING; + mTouchMode = TOUCH_MODE_DONE_WAITING; } } } @@ -2138,7 +2139,7 @@ public abstract class AbsListView extends AdapterView implements Te boolean intercepted = mFastScroller.onTouchEvent(ev); if (intercepted) { return true; - } + } } final int action = ev.getAction(); @@ -2326,10 +2327,10 @@ public abstract class AbsListView extends AdapterView implements Te } setPressed(false); - + // Need to redraw since we probably aren't drawing the selector anymore invalidate(); - + final Handler handler = getHandler(); if (handler != null) { handler.removeCallbacks(mPendingCheckForLongPress); @@ -2373,7 +2374,7 @@ public abstract class AbsListView extends AdapterView implements Te return true; } - + @Override public void draw(Canvas canvas) { super.draw(canvas); @@ -2388,14 +2389,14 @@ public abstract class AbsListView extends AdapterView implements Te int x = (int) ev.getX(); int y = (int) ev.getY(); View v; - + if (mFastScroller != null) { boolean intercepted = mFastScroller.onInterceptTouchEvent(ev); if (intercepted) { return true; } } - + switch (action) { case MotionEvent.ACTION_DOWN: { int motionPosition = findMotionRow(y); @@ -3245,7 +3246,7 @@ public abstract class AbsListView extends AdapterView implements Te } return null; } - + /** * For filtering we proxy an input connection to an internal text editor, * and this allows the proxying to happen. @@ -3254,7 +3255,7 @@ public abstract class AbsListView extends AdapterView implements Te public boolean checkInputConnectionProxy(View view) { return view == mTextFilter; } - + /** * Creates the window for the text filter and populates it with an EditText field; * @@ -3647,7 +3648,7 @@ public abstract class AbsListView extends AdapterView implements Te mCurrentScrap = scrapViews[0]; mScrapViews = scrapViews; } - + public boolean shouldRecycleViewType(int viewType) { return viewType >= 0; } @@ -3862,8 +3863,8 @@ public abstract class AbsListView extends AdapterView implements Te private final char[] mHolder; GesturesProcessor() { - mRecognizer = LetterRecognizer.getLetterRecognizer(getContext(), - LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE); + mRecognizer = LetterRecognizers.fromType(getContext(), + LetterRecognizers.RECOGNIZER_LATIN_LOWERCASE); if (mRecognizer == null) { setGestures(GESTURES_NONE); } diff --git a/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java b/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java index 6767de632d839..7865a5c750b6b 100644 --- a/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java +++ b/tests/sketch/src/com/android/gesture/example/ContactListGestureOverlay.java @@ -33,6 +33,7 @@ import android.gesture.Gesture; import android.gesture.GestureOverlayView; import android.gesture.LetterRecognizer; import android.gesture.Prediction; +import android.gesture.LetterRecognizers; import java.util.ArrayList; @@ -57,8 +58,8 @@ public class ContactListGestureOverlay extends Activity { setContentView(R.layout.overlaydemo); // create a letter recognizer - mRecognizer = LetterRecognizer.getLetterRecognizer(this, - LetterRecognizer.RECOGNIZER_LATIN_LOWERCASE); + mRecognizer = LetterRecognizers.fromType(this, + LetterRecognizers.RECOGNIZER_LATIN_LOWERCASE); mOverlay = (GestureOverlayView) findViewById(R.id.overlay); // load the contact list diff --git a/tests/sketch/src/com/android/gesture/example/GestureEntry.java b/tests/sketch/src/com/android/gesture/example/GestureEntry.java index 3f86ed4857a9c..8dbec998cecd3 100644 --- a/tests/sketch/src/com/android/gesture/example/GestureEntry.java +++ b/tests/sketch/src/com/android/gesture/example/GestureEntry.java @@ -36,9 +36,10 @@ import android.widget.Spinner; import android.widget.AdapterView.OnItemSelectedListener; import android.gesture.Gesture; -import android.gesture.GestureLibrary; import android.gesture.GestureOverlayView; import android.gesture.Prediction; +import android.gesture.GestureLibraries; +import android.gesture.GestureLibrary; import java.io.File; import java.util.ArrayList; @@ -61,7 +62,7 @@ public class GestureEntry extends Activity { private Spinner mRecognitionResult; - private GestureLibrary mGestureLibrary; + private GestureLibrary mGestureStore; private boolean mChangedByRecognizer = false; @@ -71,8 +72,8 @@ public class GestureEntry extends Activity { setContentView(R.layout.demo); // init the gesture library - mGestureLibrary = new GestureLibrary(GESTURE_FILE_NAME); - mGestureLibrary.load(); + mGestureStore = GestureLibraries.fromFile(GESTURE_FILE_NAME); + mGestureStore.load(); // create the spinner for showing the recognition results // the spinner also allows a user to correct a prediction @@ -82,7 +83,7 @@ public class GestureEntry extends Activity { public void onItemSelected(AdapterView parent, View view, int position, long id) { // correct the recognition result by adding the new example if (!mChangedByRecognizer) { - mGestureLibrary.addGesture(parent.getSelectedItem().toString(), mGesturePad + mGestureStore.addGesture(parent.getSelectedItem().toString(), mGesturePad .getGesture()); } else { mChangedByRecognizer = false; @@ -109,7 +110,7 @@ public class GestureEntry extends Activity { public void onGestureStarted(GestureOverlayView overlay, MotionEvent event) { overlay.clear(false); } - + public void onGestureCancelled(GestureOverlayView overlay, MotionEvent event) { } }); @@ -134,7 +135,7 @@ public class GestureEntry extends Activity { .findViewById(R.id.gesturename_edit); String text = edittext.getText().toString().trim(); if (text.length() > 0) { - mGestureLibrary.addGesture(text, mGesturePad.getGesture()); + mGestureStore.addGesture(text, mGesturePad.getGesture()); } } }).setNegativeButton(R.string.newgesture_dialog_cancel, @@ -173,14 +174,14 @@ public class GestureEntry extends Activity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - mGestureLibrary.load(); + mGestureStore.load(); mGesturePad.clear(false); } @Override protected void onPause() { super.onPause(); - mGestureLibrary.save(); + mGestureStore.save(); } @Override @@ -195,12 +196,12 @@ public class GestureEntry extends Activity { if (gesture != null) { outState.putParcelable(PARCEL_KEY, gesture); } - mGestureLibrary.save(); + mGestureStore.save(); } private void recognize(Gesture gesture) { mChangedByRecognizer = true; - ArrayList predictions = mGestureLibrary.recognize(gesture); + ArrayList predictions = mGestureStore.recognize(gesture); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, predictions); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); diff --git a/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java b/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java index a561c9640889b..f5bf683513383 100755 --- a/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java +++ b/tests/sketch/src/com/android/gesture/example/GestureLibViewer.java @@ -28,8 +28,9 @@ import android.widget.Spinner; import android.widget.AdapterView.OnItemSelectedListener; import android.gesture.Gesture; -import android.gesture.GestureLibrary; import android.gesture.GestureOverlayView; +import android.gesture.GestureLibraries; +import android.gesture.GestureLibrary; import java.util.ArrayList; import java.util.Collections; @@ -45,7 +46,7 @@ public class GestureLibViewer extends Activity { private Spinner mGestureCategory; - private GestureLibrary mGesureLibrary; + private GestureLibrary mGesureStore; private ArrayList mGestures; @@ -59,15 +60,15 @@ public class GestureLibViewer extends Activity { String name = (String) mGestureCategory.getSelectedItem(); Gesture gesture = mGestures.get(mCurrentGestureIndex); - mGesureLibrary.removeGesture(name, gesture); + mGesureStore.removeGesture(name, gesture); - mGestures = mGesureLibrary.getGestures(name); + mGestures = mGesureStore.getGestures(name); if (mGestures == null) { // delete the entire entry mCurrentGestureIndex = 0; ArrayList list = new ArrayList(); - list.addAll(mGesureLibrary.getGestureEntries()); + list.addAll(mGesureStore.getGestureEntries()); Collections.sort(list); ArrayAdapter adapter = new ArrayAdapter(GestureLibViewer.this, android.R.layout.simple_spinner_item, list); @@ -83,7 +84,7 @@ public class GestureLibViewer extends Activity { } } } - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -94,19 +95,19 @@ public class GestureLibViewer extends Activity { mGesturePad.setEnabled(false); // init the gesture library - mGesureLibrary = new GestureLibrary(GestureEntry.GESTURE_FILE_NAME); - mGesureLibrary.load(); + mGesureStore = GestureLibraries.fromFile(GestureEntry.GESTURE_FILE_NAME); + mGesureStore.load(); mGestureCategory = (Spinner) findViewById(R.id.spinner); ArrayList list = new ArrayList(); - if (!mGesureLibrary.getGestureEntries().isEmpty()) { - list.addAll(mGesureLibrary.getGestureEntries()); + if (!mGesureStore.getGestureEntries().isEmpty()) { + list.addAll(mGesureStore.getGestureEntries()); Collections.sort(list); ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, list); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mGestureCategory.setAdapter(adapter); - mGestures = mGesureLibrary.getGestures(list.get(0)); + mGestures = mGesureStore.getGestures(list.get(0)); mCurrentGestureIndex = 0; Gesture gesture = mGestures.get(mCurrentGestureIndex); mGesturePad.setGesture(gesture); @@ -114,7 +115,7 @@ public class GestureLibViewer extends Activity { mGestureCategory.setOnItemSelectedListener(new OnItemSelectedListener() { public void onItemSelected(AdapterView parent, View view, int position, long id) { - mGestures = mGesureLibrary.getGestures((String) mGestureCategory.getSelectedItem()); + mGestures = mGesureStore.getGestures((String) mGestureCategory.getSelectedItem()); if (!mGestures.isEmpty()) { mCurrentGestureIndex = 0; Gesture gesture = mGestures.get(mCurrentGestureIndex); @@ -160,7 +161,7 @@ public class GestureLibViewer extends Activity { @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - mGesureLibrary.save(); + mGesureStore.save(); setResult(RESULT_OK); finish(); return true; @@ -172,12 +173,12 @@ public class GestureLibViewer extends Activity { @Override protected void onPause() { super.onPause(); - mGesureLibrary.save(); + mGesureStore.save(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - mGesureLibrary.save(); + mGesureStore.save(); } } From 6d892b60dde12aff270df1b3d2809336c3fc1265 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 29 May 2009 14:55:06 -0700 Subject: [PATCH 40/43] log the geomerty of surfaces causing an out of memory in SurfaceFlinger. --- libs/surfaceflinger/LayerBitmap.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp index e84435088a7c4..397ddc82e12a1 100644 --- a/libs/surfaceflinger/LayerBitmap.cpp +++ b/libs/surfaceflinger/LayerBitmap.cpp @@ -114,7 +114,9 @@ status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, } if (mBitsMemory==0 || mSurface.data==0) { - LOGE("not enough memory for layer bitmap size=%u", size); + LOGE("not enough memory for layer bitmap " + "size=%u (w=%d, h=%d, stride=%d, format=%d)", + size, int(w), int(h), int(stride), int(format)); allocator->dump("LayerBitmap"); mSurface.data = 0; mSize = -1U; From 8d78756c160bda736cccef9ca1a6e2d6a159ac42 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Fri, 29 May 2009 15:02:55 -0700 Subject: [PATCH 41/43] Fix the build. --- api/current.xml | 494 +++++++++++++++--- .../android/gesture/GestureOverlayView.java | 21 +- 2 files changed, 446 insertions(+), 69 deletions(-) diff --git a/api/current.xml b/api/current.xml index c67c9c4218597..f572a4d23e942 100644 --- a/api/current.xml +++ b/api/current.xml @@ -46404,10 +46404,75 @@ > - + + + + + + + + + + + + + + + + + + + + + + - - + + - + + - - - - - - - @@ -47245,6 +47296,259 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + = GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) { areaToRefresh = mInvalidRect; @@ -538,16 +539,16 @@ public class GestureOverlayView extends FrameLayout { final int border = mInvalidateExtraBorder; areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border, (int) mCurveEndX + border, (int) mCurveEndY + border); - + float cX = mCurveEndX = (x + previousX) / 2; float cY = mCurveEndY = (y + previousY) / 2; mPath.quadTo(previousX, previousY, cX, cY); - + // union with the control point of the new curve areaToRefresh.union((int) previousX - border, (int) previousY - border, (int) previousX + border, (int) previousY + border); - + // union with the end point of the new curve areaToRefresh.union((int) cX - border, (int) cY - border, (int) cX + border, (int) cY + border); @@ -618,7 +619,7 @@ public class GestureOverlayView extends FrameLayout { cancelGesture(event); } - mStrokeBuffer.clear(); + mStrokeBuffer.clear(); mIsGesturing = false; } @@ -678,7 +679,7 @@ public class GestureOverlayView extends FrameLayout { setPaintAlpha(255); } - invalidate(); + invalidate(); } } From 34b234d53f9658ff7206dad6158993a1d197ffa7 Mon Sep 17 00:00:00 2001 From: Mitsuru Oshima Date: Thu, 28 May 2009 19:37:56 -0700 Subject: [PATCH 42/43] * Chagned RecognitionService interface to use more complex RecognitionResult instead of String. --- .../android/speech/IRecognitionListener.aidl | 19 ++- .../android/speech/IRecognitionService.aidl | 3 + .../android/speech/RecognitionResult.aidl | 19 +++ .../android/speech/RecognitionResult.java | 151 ++++++++++++++++++ .../speech/RecognitionServiceUtil.java | 12 +- 5 files changed, 196 insertions(+), 8 deletions(-) create mode 100644 core/java/android/speech/RecognitionResult.aidl create mode 100644 core/java/android/speech/RecognitionResult.java diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl index 6ed32b502b424..b4abfda30aed0 100644 --- a/core/java/android/speech/IRecognitionListener.aidl +++ b/core/java/android/speech/IRecognitionListener.aidl @@ -17,6 +17,7 @@ package android.speech; import android.os.Bundle; +import android.speech.RecognitionResult; /** * Listener for speech recognition events, used with RecognitionService. @@ -43,13 +44,19 @@ interface IRecognitionListener { /** Called after the user stops speaking. */ void onEndOfSpeech(); - /** A network or recognition error occurred. */ - void onError(in String error); + /** + * A network or recognition error occurred. + * TODO: right now, the error code is given in voice search package + * (vendor/google/apps/src/com/google/android/voicesearch/speechservice/SpeechServiceListener.java) + * we need to find a place to define common error code. + */ + void onError(in int error); /** - * Called when recognition transcripts are ready. - * results: an ordered list of the most likely transcripts (N-best list). - * @hide + * Called when recognition results are ready. + * @param results: an ordered list of the most likely results (N-best list). + * @param key: a key associated with the results. The same results can + * be retrieved asynchronously later using the key, if available. */ - void onResults(in List results); + void onResults(in List results, long key); } diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl index 36d12e9aae643..a18c380c9e707 100644 --- a/core/java/android/speech/IRecognitionService.aidl +++ b/core/java/android/speech/IRecognitionService.aidl @@ -18,6 +18,7 @@ package android.speech; import android.content.Intent; import android.speech.IRecognitionListener; +import android.speech.RecognitionResult; // A Service interface to speech recognition. Call startListening when // you want to begin capturing audio; RecognitionService will automatically @@ -29,6 +30,8 @@ interface IRecognitionService { // see RecognizerIntent.java for constants used to specify the intent. void startListening(in Intent recognizerIntent, in IRecognitionListener listener); + + List getRecognitionResults(in long key); void cancel(); } diff --git a/core/java/android/speech/RecognitionResult.aidl b/core/java/android/speech/RecognitionResult.aidl new file mode 100644 index 0000000000000..59e53ab86fd18 --- /dev/null +++ b/core/java/android/speech/RecognitionResult.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.speech; + +parcelable RecognitionResult; diff --git a/core/java/android/speech/RecognitionResult.java b/core/java/android/speech/RecognitionResult.java new file mode 100644 index 0000000000000..c3ac48404f6ee --- /dev/null +++ b/core/java/android/speech/RecognitionResult.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package android.speech; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * RecognitionResult is a passive object that stores a single recognized + * query and its search result. + * TODO: revisit and improve. May be we should have a separate result + * object for each type, and put them (type/value) in bundle? + * + * {@hide} + */ +public class RecognitionResult implements Parcelable { + + /** + * Type of the recognition results. + */ + public static final int RAW_RECOGNITION_RESULT = 0; + public static final int WEB_SEARCH_RESULT = 1; + public static final int CONTACT_RESULT = 2; + + /** + * A factory method to create a raw RecognitionResult + * + * @param sentence the recognized text. + */ + public static RecognitionResult newRawRecognitionResult(String sentence) { + return new RecognitionResult(RAW_RECOGNITION_RESULT, sentence, null, null); + } + + /** + * A factory method to create RecognitionResult for contacts. + * + * @param contact the contact name. + * @param phoneType the phone type. + */ + public static RecognitionResult newContactResult(String contact, int phoneType) { + return new RecognitionResult(CONTACT_RESULT, contact, phoneType); + } + + /** + * A factory method to create a RecognitionResult for Web Search Query. + * + * @param query the query string. + * @param html the html page of the search result. + * @param url the url that performs the search with the query. + */ + public static RecognitionResult newWebResult(String query, String html, String url) { + return new RecognitionResult(WEB_SEARCH_RESULT, query, html, url); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + + public RecognitionResult createFromParcel(Parcel in) { + return new RecognitionResult(in); + } + + public RecognitionResult[] newArray(int size) { + return new RecognitionResult[size]; + } + }; + + /** + * Result type. + */ + public final int mResultType; + + /** + * The recognized string when mResultType is WEB_SEARCH_RESULT. + * The name of the contact when mResultType is CONTACT_RESULT. + */ + public final String mText; + + /** + * The HTML result page for the query. If this is null, then the + * application must use the url field to get the HTML result page. + */ + public final String mHtml; + + /** + * The url to get the result page for the query string. The + * application must use this url instead of performing the search + * with the query. + */ + public final String mUrl; + + /** Phone number type. This is valid only when mResultType == CONTACT_RESULT */ + public final int mPhoneType; + + private RecognitionResult(int type, String query, String html, String url) { + mResultType = type; + mText = query; + mHtml = html; + mUrl = url; + mPhoneType = -1; + } + + private RecognitionResult(int type, String query, int at) { + mResultType = type; + mText = query; + mPhoneType = at; + mHtml = null; + mUrl = null; + } + + private RecognitionResult(Parcel in) { + mResultType = in.readInt(); + mText = in.readString(); + mHtml= in.readString(); + mUrl= in.readString(); + mPhoneType = in.readInt(); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mResultType); + out.writeString(mText); + out.writeString(mHtml); + out.writeString(mUrl); + out.writeInt(mPhoneType); + } + + + @Override + public String toString() { + String resultType[] = { "RAW", "WEB", "CONTACT" }; + return "[type=" + resultType[mResultType] + + ", text=" + mText+ ", mUrl=" + mUrl + ", html=" + mHtml + "]"; + } + + public int describeContents() { + // no special description + return 0; + } +} diff --git a/core/java/android/speech/RecognitionServiceUtil.java b/core/java/android/speech/RecognitionServiceUtil.java index 650c0fd248817..a8c78684f6cdc 100644 --- a/core/java/android/speech/RecognitionServiceUtil.java +++ b/core/java/android/speech/RecognitionServiceUtil.java @@ -21,6 +21,9 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.speech.RecognitionResult; +import android.util.Log; import java.util.List; @@ -56,6 +59,11 @@ public class RecognitionServiceUtil { public static final Intent sDefaultIntent = new Intent( RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + // Recognize request parameters + public static final String USE_LOCATION = "useLocation"; + public static final String CONTACT_AUTH_TOKEN = "contactAuthToken"; + + // Bundles public static final String NOISE_LEVEL = "NoiseLevel"; public static final String SIGNAL_NOISE_RATIO = "SignalNoiseRatio"; @@ -72,8 +80,8 @@ public class RecognitionServiceUtil { public void onRmsChanged(float rmsdB) {} public void onBufferReceived(byte[] buf) {} public void onEndOfSpeech() {} - public void onError(String error) {} - public void onResults(List results) {} + public void onError(int error) {} + public void onResults(List results, long key) {} } /** From ef354fdd634de93004fddd7966d70f70a1351ca8 Mon Sep 17 00:00:00 2001 From: jsh Date: Fri, 29 May 2009 16:13:48 -0700 Subject: [PATCH 43/43] Fix concat SMS for GSM. Bug 1883998: We only support sending 8-bit references for now. Bug 1885080: Also fix the GsmAlphabetTest, which started failing when SmsHeader was re-worked. --- .../android/internal/telephony/gsm/GsmSMSDispatcher.java | 8 +++++++- .../com/android/internal/telephony/GsmAlphabetTest.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 347b3afc7a96c..8bf5d1903cb5a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -152,7 +152,13 @@ final class GsmSMSDispatcher extends SMSDispatcher { concatRef.refNumber = refNumber; concatRef.seqNumber = i + 1; // 1-based sequence concatRef.msgCount = msgCount; - concatRef.isEightBits = false; + // TODO: We currently set this to true since our messaging app will never + // send more than 255 parts (it converts the message to MMS well before that). + // However, we should support 3rd party messaging apps that might need 16-bit + // references + // Note: It's not sufficient to just flip this bit to true; it will have + // ripple effects (several calculations assume 8-bit ref). + concatRef.isEightBits = true; SmsHeader smsHeader = new SmsHeader(); smsHeader.concatRef = concatRef; diff --git a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java index e2336f8832c99..3a9c5116f1605 100644 --- a/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java +++ b/tests/CoreTests/com/android/internal/telephony/GsmAlphabetTest.java @@ -41,7 +41,7 @@ public class GsmAlphabetTest extends TestCase { SmsHeader.toByteArray(header)); int septetCount = GsmAlphabet.countGsmSeptets(message, false); String parsedMessage = GsmAlphabet.gsm7BitPackedToString( - userData, SmsHeader.toByteArray(header).length+1, septetCount, 1); + userData, SmsHeader.toByteArray(header).length+2, septetCount, 1); assertEquals(message, parsedMessage); }