Merge "Enhanced VelocityTracker for > 5 pointers and fixed bugs." into gingerbread
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index b7a750b..00063af 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -403,7 +403,7 @@
*
* <p class="caution">Note that the system calls this on your
* service's main thread. A service's main thread is the same
- * thread where UI operations place for Activities running in the
+ * thread where UI operations take place for Activities running in the
* same process. You should always avoid stalling the main
* thread's event loop. When doing long-running operations,
* network calls, or heavy disk I/O, you should kick off a new
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 73c4011..0b35d8b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -587,7 +587,7 @@
* location from the apk location at the given file path.
* @param packageFilePath file location of the apk
* @param flags Special parse flags
- * @return PackageLite object with package information.
+ * @return PackageLite object with package information or null on failure.
*/
public static PackageLite parsePackageLite(String packageFilePath, int flags) {
XmlResourceParser parser = null;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7bb89f5..ea26207 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3373,6 +3373,14 @@
public static final String THROTTLE_MAX_NTP_CACHE_AGE_SEC =
"throttle_max_ntp_cache_age_sec";
+ /**
+ * The maximum size, in bytes, of a download that the download manager will transfer over
+ * a non-wifi connection.
+ * @hide
+ */
+ public static final String DOWNLOAD_MAX_BYTES_OVER_MOBILE =
+ "download_manager_max_bytes_over_mobile";
+
/**
* @hide
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index d2563a8..5dd45f9 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -916,72 +916,13 @@
out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
if (view != null) {
- final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- forceLayout(view);
- return null;
- }
-
- private void forceLayout(View view) {
- view.forceLayout();
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
- final int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- forceLayout(group.getChildAt(i));
- }
- }
- }
-
- public void run(Void... data) {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
-
- public void post(Void... data) {
- }
- });
-
- final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- return null;
- }
-
- public void run(Void... data) {
- view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
- }
-
- public void post(Void... data) {
- }
- });
-
- final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
- public Object[] pre() {
- final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
- final Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels,
- metrics.heightPixels, Bitmap.Config.RGB_565);
- final Canvas canvas = new Canvas(bitmap);
- return new Object[] { bitmap, canvas };
- }
-
- public void run(Object... data) {
- view.draw((Canvas) data[1]);
- }
-
- public void post(Object... data) {
- ((Bitmap) data[0]).recycle();
- }
- });
-
- out.write(String.valueOf(durationMeasure));
- out.write(' ');
- out.write(String.valueOf(durationLayout));
- out.write(' ');
- out.write(String.valueOf(durationDraw));
- out.newLine();
+ profileViewAndChildren(view, out);
} else {
out.write("-1 -1 -1");
out.newLine();
}
+ out.write("DONE.");
+ out.newLine();
} catch (Exception e) {
android.util.Log.w("View", "Problem profiling the view:", e);
} finally {
@@ -991,6 +932,82 @@
}
}
+ private static void profileViewAndChildren(final View view, BufferedWriter out)
+ throws IOException {
+ final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
+
+ private void forceLayout(View view) {
+ view.forceLayout();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ forceLayout(group.getChildAt(i));
+ }
+ }
+ }
+
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
+
+ public void post(Void... data) {
+ }
+ });
+
+ final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ return null;
+ }
+
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
+
+ public void post(Void... data) {
+ }
+ });
+
+ final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
+ public Object[] pre() {
+ final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
+ final Bitmap bitmap =
+ Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels,
+ Bitmap.Config.RGB_565);
+ final Canvas canvas = new Canvas(bitmap);
+ return new Object[] {
+ bitmap, canvas
+ };
+ }
+
+ public void run(Object... data) {
+ view.draw((Canvas) data[1]);
+ }
+
+ public void post(Object... data) {
+ ((Bitmap) data[0]).recycle();
+ }
+ });
+
+ out.write(String.valueOf(durationMeasure));
+ out.write(' ');
+ out.write(String.valueOf(durationLayout));
+ out.write(' ');
+ out.write(String.valueOf(durationDraw));
+ out.newLine();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ profileViewAndChildren(group.getChildAt(i), out);
+ }
+ }
+ }
+
interface ViewOperation<T> {
T[] pre();
void run(T... data);
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 01940e8..f37a122 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -163,12 +163,11 @@
'img':"devphone-large.png",
'title':"Android Dev Phones",
'desc': "<p>Run and debug your Android applications directly on one of these "
- + "device. Modify and rebuild the Android operating system, and flash it onto "
- + "the phone. The Android Dev Phones are carrier independent, and available for "
- + "purchase by any developer registered with <a "
- + "href='http://market.android.com/publish'>Android Market</a>.</p><p><a "
- + "href='/guide/developing/device.html#dev-phone-1'>Learn more about the "
- + "Android Dev Phones »</a></p>"
+ + "devices. Modify and rebuild the Android operating system, and flash it onto "
+ + "the phone. The Android Dev Phones are carrier-independent, and available for "
+ + "purchase by developers through their Android Market publisher accounts.</p><p> "
+ + "<a href='http://market.android.com/publish'>Visit Android Market "
+ + "to learn more »</a></p>"
},
'mapskey': {
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index d4b6db5..ec47796 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,8 +52,9 @@
<div class="dashboard-panel">
<img alt="" height="250" width="460"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.3,18.9,22.1,55.5,3.3&chl=Other*|
-Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:15.3,20.3,0.2,59.7,4.5&chl=
+Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,
+6fad0c" />
<table>
<tr>
@@ -61,14 +62,14 @@
<th>API Level</th>
<th>Distribution</th>
</tr>
-<tr><td>Android 1.5</td><td>3</td><td>18.9%</td></tr>
-<tr><td>Android 1.6</td><td>4</td><td>22.1%</td></tr>
-<tr><td>Android 2.1</td><td>7</td><td>55.5%</td></tr>
-<tr><td>Android 2.2</td><td>8</td><td>3.3%</td></tr>
+<tr><td>Android 1.5</td><td>3</td><td>15.3%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>20.3%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>59.7%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>4.5%</td></tr>
</table>
-<p><em>Data collected during two weeks ending on July 15, 2010</em></p>
-<p style="font-size:.9em">* <em>Other: 0.3% of devices running obsolete versions</em></p>
+<p><em>Data collected during two weeks ending on August 2, 2010</em></p>
+<p style="font-size:.9em">* <em>Other: 0.2% of devices running obsolete versions</em></p>
</div><!-- end dashboard-panel -->
@@ -94,21 +95,20 @@
<div class="dashboard-panel">
-<img alt="" height="265" width="700" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=700x265&chxt=x,y,r&chxr=0,0,10%7C1,0,100%7C2,0,
-100&chxl=0%3A%7C2010/02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%
-7C2010/07/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%
-7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10&chxtc=0,5&chd=t:99.0,99.2,99.4,99.5,99.6,99.6,99.6,99.7,100.6
-,101.1,99.9%7C63.4,62.5,61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6%7C22.6,23.2,24.3,25.4,29.4,30.2
-,32.7,35.3,46.2,51.3,55.1%7C0.0,0.0,0.0,0.0,4.0,28.3,32.0,34.9,45.9,51.0,54.9%7C0.0,0.0,0.0,0.0,0.0,
-0.0,0.0,0.0,0.8,1.2,1.8&chm=tAndroid%201.5,7caa36,0,0,15,,t::-5%7Cb,c3df9b,0,1,0%7CtAndroid%201.6,
-638d23,1,0,15,,t::-5%7Cb,b0db6e,1,2,0%7CtAndroid%202.0.1,496c13,2,0,15,,t::-5%7Cb,9ddb3d,2,3,0%
-7CtAndroid%202.1,2f4708,3,5,15,,t::-5%7Cb,89cf19,3,4,0%7CB,6fad0c,4,5,0&chg=9,25&chdl=Android%201.5%
-20(API%20Level%203)%7CAndroid%201.6%20(API%20Level%204)%7CAndroid%202.0.1%20(API%20Level%206)%
-7CAndroid%202.1%20(API%20Level%207)%7CAndroid%202.2%20(API%20Level %208)&chco=add274,
-9ad145,84c323,6ba213,507d08" />
+<img alt="" height="250" width="660" style="padding:5px;background:#fff"
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
+chxl=0%3A%7C2010/02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/
+01%7C07/15%7C2010/08/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25
+%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.0,99.2,99.4,99.5,99.6,99.6,
+99.6,99.7,100.6,101.1,99.9,100.0,100.0|63.4,62.5,61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6,81.1,
+84.5|22.6,23.2,24.3,25.4,29.4,30.2,32.7,35.3,46.2,51.3,55.1,59.0,64.1|0.0,0.0,0.0,0.0,4.0,28.3,32.0,
+34.9,45.9,51.0,54.9,58.8,64.0|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.8,1.2,1.8,3.3,4.3&chm=tAndroid%201.5
+,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,638d23,1,0,15,,t::-5|b,b0db6e,1,2,0|tAndroid%202
+.0.1,496c13,2,0,15,,t::-5|b,9ddb3d,2,3,0|tAndroid%202.1,2f4708,3,5,15,,t::-5|b,89cf19,3,4,0|B,6fad0c
+,4,5,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.0.1|Android%202.1|Android%202.2&chco=
+add274,9ad145,84c323,6ba213,507d08" />
-<p><em>Last historical dataset collected during two weeks ending on July 1, 2010</em></p>
+<p><em>Last historical dataset collected during two weeks ending on August 2, 2010</em></p>
</div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index b20b17d..90f3f1a 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -49,8 +49,8 @@
<div class="dashboard-panel">
<img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.8,51.5,46.6&chl=Small%20/%20ldpi|
-Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:2.3,0.4,45.9,51.2&chl=Small%20/%
+20ldpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -60,22 +60,22 @@
<th scope="col">High Density</th>
</tr>
<tr><th scope="row">Small</th>
-<td class='cent hi'>1.8%</td>
+<td class='cent hi'>2.3%</td>
<td></td>
<td></td>
</tr>
<tr><th scope="row">Normal</th>
-<td></td>
-<td class='cent hi'>51.5%</td>
-<td class='cent hi'>46.6%</td>
+<td class='cent '>0.4%</td>
+<td class='cent hi'>45.9%</td>
+<td class='cent hi'>51.2%</td>
</tr>
<tr><th scope="row">Large</th>
<td></td>
<td></td>
<td></td>
-</tr>
+</tr>
</table>
-<p><em>Data collected during two weeks ending on July 15, 2010</em></p>
+<p><em>Data collected during two weeks ending on August 2, 2010</em></p>
</div>
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 628200d..f1d45d2 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -57,11 +57,10 @@
bool mCollectStats;
bool mTrackMaxAmplitude;
- int64_t mTotalReadTimeUs;
- int64_t mTotalReadBytes;
- int64_t mTotalReads;
int64_t mStartTimeUs;
int16_t mMaxAmplitude;
+ int64_t mPrevSampleTimeUs;
+ int64_t mNumLostFrames;
MediaBufferGroup *mGroup;
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index d0c2dca..2e1e8d8 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -75,6 +75,7 @@
uint32_t mInterleaveDurationUs;
int32_t mTimeScale;
int64_t mStartTimestampUs;
+
Mutex mLock;
List<Track *> mTracks;
@@ -87,6 +88,46 @@
size_t numTracks();
int64_t estimateMoovBoxSize(int32_t bitRate);
+ struct Chunk {
+ Track *mTrack; // Owner
+ int64_t mTimeStampUs; // Timestamp of the 1st sample
+ List<MediaBuffer *> mSamples; // Sample data
+
+ // Convenient constructor
+ Chunk(Track *track, int64_t timeUs, List<MediaBuffer *> samples)
+ : mTrack(track), mTimeStampUs(timeUs), mSamples(samples) {
+ }
+
+ };
+ struct ChunkInfo {
+ Track *mTrack; // Owner
+ List<Chunk> mChunks; // Remaining chunks to be written
+ };
+
+ bool mIsFirstChunk;
+ volatile bool mDone; // Writer thread is done?
+ pthread_t mThread; // Thread id for the writer
+ List<ChunkInfo> mChunkInfos; // Chunk infos
+ Condition mChunkReadyCondition; // Signal that chunks are available
+
+ // Writer thread handling
+ status_t startWriterThread();
+ void stopWriterThread();
+ static void *ThreadWrapper(void *me);
+ void threadFunc();
+
+ // Buffer a single chunk to be written out later.
+ void bufferChunk(const Chunk& chunk);
+
+ // Write all buffered chunks from all tracks
+ void writeChunks();
+
+ // Write a chunk if there is one
+ status_t writeOneChunk();
+
+ // Write the first chunk from the given ChunkInfo.
+ void writeFirstChunk(ChunkInfo* info);
+
void lock();
void unlock();
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 50c0edc..99978e8 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -26,8 +26,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <cutils/properties.h>
-#include <sys/time.h>
-#include <time.h>
+#include <stdlib.h>
namespace android {
@@ -35,9 +34,8 @@
int inputSource, uint32_t sampleRate, uint32_t channels)
: mStarted(false),
mCollectStats(false),
- mTotalReadTimeUs(0),
- mTotalReadBytes(0),
- mTotalReads(0),
+ mPrevSampleTimeUs(0),
+ mNumLostFrames(0),
mGroup(NULL) {
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
@@ -110,10 +108,7 @@
mStarted = false;
if (mCollectStats) {
- LOGI("%lld reads: %.2f bps in %lld us",
- mTotalReads,
- (mTotalReadBytes * 8000000.0) / mTotalReadTimeUs,
- mTotalReadTimeUs);
+ LOGI("Total lost audio frames: %lld", mNumLostFrames);
}
return OK;
@@ -129,67 +124,113 @@
return meta;
}
+/*
+ * Returns -1 if frame skipping request is too long.
+ * Returns 0 if there is no need to skip frames.
+ * Returns 1 if we need to skip frames.
+ */
+static int skipFrame(int64_t timestampUs,
+ const MediaSource::ReadOptions *options) {
+
+ int64_t skipFrameUs;
+ if (!options || !options->getSkipFrame(&skipFrameUs)) {
+ return 0;
+ }
+
+ if (skipFrameUs <= timestampUs) {
+ return 0;
+ }
+
+ // Safe guard against the abuse of the kSkipFrame_Option.
+ if (skipFrameUs - timestampUs >= 1E6) {
+ LOGE("Frame skipping requested is way too long: %lld us",
+ skipFrameUs - timestampUs);
+
+ return -1;
+ }
+
+ LOGV("skipFrame: %lld us > timestamp: %lld us",
+ skipFrameUs, timestampUs);
+
+ return 1;
+
+}
+
status_t AudioSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
- ++mTotalReads;
MediaBuffer *buffer;
CHECK_EQ(mGroup->acquire_buffer(&buffer), OK);
+ int err = 0;
while (mStarted) {
+
uint32_t numFramesRecorded;
mRecord->getPosition(&numFramesRecorded);
- int64_t latency = mRecord->latency() * 1000;
- int64_t readTime = systemTime() / 1000;
- if (numFramesRecorded == 0) {
+ if (numFramesRecorded == 0 && mPrevSampleTimeUs == 0) {
// Initial delay
if (mStartTimeUs > 0) {
- mStartTimeUs = readTime - mStartTimeUs;
+ mStartTimeUs = systemTime() / 1000 - mStartTimeUs;
} else {
- mStartTimeUs += latency;
+ // Assume latency is constant.
+ mStartTimeUs += mRecord->latency() * 1000;
}
- }
-
- ssize_t n = 0;
- if (mCollectStats) {
- n = mRecord->read(buffer->data(), buffer->size());
- int64_t endTime = systemTime() / 1000;
- mTotalReadTimeUs += (endTime - readTime);
- if (n >= 0) {
- mTotalReadBytes += n;
- }
- } else {
- n = mRecord->read(buffer->data(), buffer->size());
- }
-
- if (n < 0) {
- buffer->release();
- buffer = NULL;
-
- return (status_t)n;
+ mPrevSampleTimeUs = mStartTimeUs;
}
uint32_t sampleRate = mRecord->getSampleRate();
- int64_t timestampUs = (1000000LL * numFramesRecorded) / sampleRate +
- mStartTimeUs;
- int64_t skipFrameUs;
- if (!options || !options->getSkipFrame(&skipFrameUs)) {
- skipFrameUs = timestampUs; // Don't skip frame
- }
- if (skipFrameUs > timestampUs) {
- // Safe guard against the abuse of the kSkipFrame_Option.
- if (skipFrameUs - timestampUs >= 1E6) {
- LOGE("Frame skipping requested is way too long: %lld us",
- skipFrameUs - timestampUs);
+ // Insert null frames when lost frames are detected.
+ int64_t timestampUs = mPrevSampleTimeUs;
+ uint32_t numLostBytes = mRecord->getInputFramesLost() << 1;
+#if 0
+ // Simulate lost frames
+ numLostBytes = ((rand() * 1.0 / RAND_MAX)) * kMaxBufferSize;
+ numLostBytes &= 0xFFFFFFFE; // Alignment request
+
+ // Reduce the chance to lose
+ if (rand() * 1.0 / RAND_MAX >= 0.05) {
+ numLostBytes = 0;
+ }
+#endif
+ if (numLostBytes > 0) {
+ // Not expect too many lost frames!
+ CHECK(numLostBytes <= kMaxBufferSize);
+
+ timestampUs += (1000000LL * numLostBytes >> 1) / sampleRate;
+ CHECK(timestampUs > mPrevSampleTimeUs);
+ if (mCollectStats) {
+ mNumLostFrames += (numLostBytes >> 1);
+ }
+ if ((err = skipFrame(timestampUs, options)) == -1) {
buffer->release();
return UNKNOWN_ERROR;
+ } else if (err != 0) {
+ continue;
}
- LOGV("skipFrame: %lld us > timestamp: %lld us, samples %d",
- skipFrameUs, timestampUs, numFramesRecorded);
+ memset(buffer->data(), 0, numLostBytes);
+ buffer->set_range(0, numLostBytes);
+ buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
+ mPrevSampleTimeUs = timestampUs;
+ *out = buffer;
+ return OK;
+ }
+
+ ssize_t n = mRecord->read(buffer->data(), buffer->size());
+ if (n < 0) {
+ buffer->release();
+ return (status_t)n;
+ }
+
+ int64_t recordDurationUs = (1000000LL * n >> 1) / sampleRate;
+ timestampUs += recordDurationUs;
+ if ((err = skipFrame(timestampUs, options)) == -1) {
+ buffer->release();
+ return UNKNOWN_ERROR;
+ } else if (err != 0) {
continue;
}
@@ -197,7 +238,13 @@
trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
}
- buffer->meta_data()->setInt64(kKeyTime, timestampUs);
+ buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
+ CHECK(timestampUs > mPrevSampleTimeUs);
+ if (mNumLostFrames == 0) {
+ CHECK_EQ(mPrevSampleTimeUs,
+ mStartTimeUs + (1000000LL * numFramesRecorded) / sampleRate);
+ }
+ mPrevSampleTimeUs = timestampUs;
LOGV("initial delay: %lld, sample rate: %d, timestamp: %lld",
mStartTimeUs, sampleRate, timestampUs);
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 9f712c3..4928951 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -52,6 +52,11 @@
int64_t getDurationUs() const;
int64_t getEstimatedTrackSizeBytes() const;
void writeTrackHeader(int32_t trackID, bool use32BitOffset = true);
+ void bufferChunk(int64_t timestampUs);
+ bool isAvc() const { return mIsAvc; }
+ bool isAudio() const { return mIsAudio; }
+ bool isMPEG4() const { return mIsMPEG4; }
+ void addChunkOffset(off_t offset) { mChunkOffsets.push_back(offset); }
private:
MPEG4Writer *mOwner;
@@ -60,8 +65,12 @@
volatile bool mDone;
volatile bool mPaused;
volatile bool mResumed;
+ bool mIsAvc;
+ bool mIsAudio;
+ bool mIsMPEG4;
int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
+ int64_t mMaxWriteTimeUs;
int32_t mTimeScale;
pthread_t mThread;
@@ -117,7 +126,6 @@
status_t makeAVCCodecSpecificData(
const uint8_t *data, size_t size);
- void writeOneChunk(bool isAvc);
// Track authoring progress status
void trackProgressStatus(int64_t timeUs, status_t err = OK);
@@ -320,10 +328,17 @@
} else {
write("\x00\x00\x00\x01mdat????????", 16);
}
- status_t err = startTracks(param);
+
+ status_t err = startWriterThread();
if (err != OK) {
return err;
}
+
+ err = startTracks(param);
+ if (err != OK) {
+ return err;
+ }
+
mStarted = true;
return OK;
}
@@ -339,6 +354,20 @@
}
}
+void MPEG4Writer::stopWriterThread() {
+ LOGV("stopWriterThread");
+
+ {
+ Mutex::Autolock autolock(mLock);
+
+ mDone = true;
+ mChunkReadyCondition.signal();
+ }
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+}
+
void MPEG4Writer::stop() {
if (mFile == NULL) {
return;
@@ -355,6 +384,7 @@
}
}
+ stopWriterThread();
// Fix up the size of the 'mdat' chunk.
if (mUse32BitOffset) {
@@ -693,6 +723,14 @@
if (!mMeta->findInt32(kKeyTimeScale, &mTimeScale)) {
mTimeScale = 1000;
}
+
+ const char *mime;
+ mMeta->findCString(kKeyMIMEType, &mime);
+ mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+ mIsAudio = !strncasecmp(mime, "audio/", 6);
+ mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
+ !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+
CHECK(mTimeScale > 0);
}
@@ -751,6 +789,148 @@
}
}
+// static
+void *MPEG4Writer::ThreadWrapper(void *me) {
+ LOGV("ThreadWrapper: %p", me);
+ MPEG4Writer *writer = static_cast<MPEG4Writer *>(me);
+ writer->threadFunc();
+ return NULL;
+}
+
+void MPEG4Writer::bufferChunk(const Chunk& chunk) {
+ LOGV("bufferChunk: %p", chunk.mTrack);
+ Mutex::Autolock autolock(mLock);
+ CHECK_EQ(mDone, false);
+
+ for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
+ it != mChunkInfos.end(); ++it) {
+
+ if (chunk.mTrack == it->mTrack) { // Found owner
+ it->mChunks.push_back(chunk);
+ mChunkReadyCondition.signal();
+ return;
+ }
+ }
+
+ CHECK("Received a chunk for a unknown track" == 0);
+}
+
+void MPEG4Writer::writeFirstChunk(ChunkInfo* info) {
+ LOGV("writeFirstChunk: %p", info->mTrack);
+
+ List<Chunk>::iterator chunkIt = info->mChunks.begin();
+ for (List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
+ it != chunkIt->mSamples.end(); ++it) {
+
+ off_t offset = info->mTrack->isAvc()
+ ? addLengthPrefixedSample_l(*it)
+ : addSample_l(*it);
+ if (it == chunkIt->mSamples.begin()) {
+ info->mTrack->addChunkOffset(offset);
+ }
+ }
+
+ // Done with the current chunk.
+ // Release all the samples in this chunk.
+ while (!chunkIt->mSamples.empty()) {
+ List<MediaBuffer *>::iterator it = chunkIt->mSamples.begin();
+ (*it)->release();
+ (*it) = NULL;
+ chunkIt->mSamples.erase(it);
+ }
+ chunkIt->mSamples.clear();
+ info->mChunks.erase(chunkIt);
+}
+
+void MPEG4Writer::writeChunks() {
+ LOGV("writeChunks");
+ size_t outstandingChunks = 0;
+ while (!mChunkInfos.empty()) {
+ List<ChunkInfo>::iterator it = mChunkInfos.begin();
+ while (!it->mChunks.empty()) {
+ CHECK_EQ(OK, writeOneChunk());
+ ++outstandingChunks;
+ }
+ it->mTrack = NULL;
+ mChunkInfos.erase(it);
+ }
+ mChunkInfos.clear();
+ LOGD("%d chunks are written in the last batch", outstandingChunks);
+}
+
+status_t MPEG4Writer::writeOneChunk() {
+ LOGV("writeOneChunk");
+
+ // Find the smallest timestamp, and write that chunk out
+ // XXX: What if some track is just too slow?
+ int64_t minTimestampUs = 0x7FFFFFFFFFFFFFFFLL;
+ Track *track = NULL;
+ for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
+ it != mChunkInfos.end(); ++it) {
+ if (!it->mChunks.empty()) {
+ List<Chunk>::iterator chunkIt = it->mChunks.begin();
+ if (chunkIt->mTimeStampUs < minTimestampUs) {
+ minTimestampUs = chunkIt->mTimeStampUs;
+ track = it->mTrack;
+ }
+ }
+ }
+
+ if (track == NULL) {
+ LOGV("Nothing to be written after all");
+ return OK;
+ }
+
+ if (mIsFirstChunk) {
+ mIsFirstChunk = false;
+ }
+ for (List<ChunkInfo>::iterator it = mChunkInfos.begin();
+ it != mChunkInfos.end(); ++it) {
+ if (it->mTrack == track) {
+ writeFirstChunk(&(*it));
+ }
+ }
+ return OK;
+}
+
+void MPEG4Writer::threadFunc() {
+ LOGV("threadFunc");
+
+ while (!mDone) {
+ {
+ Mutex::Autolock autolock(mLock);
+ mChunkReadyCondition.wait(mLock);
+ CHECK_EQ(writeOneChunk(), OK);
+ }
+ }
+
+ {
+ // Write ALL samples
+ Mutex::Autolock autolock(mLock);
+ writeChunks();
+ }
+}
+
+status_t MPEG4Writer::startWriterThread() {
+ LOGV("startWriterThread");
+
+ mDone = false;
+ mIsFirstChunk = true;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ ChunkInfo info;
+ info.mTrack = *it;
+ mChunkInfos.push_back(info);
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+ pthread_attr_destroy(&attr);
+ return OK;
+}
+
status_t MPEG4Writer::Track::start(MetaData *params) {
if (!mDone && mPaused) {
mPaused = false;
@@ -926,13 +1106,6 @@
}
void MPEG4Writer::Track::threadEntry() {
- sp<MetaData> meta = mSource->getFormat();
- const char *mime;
- meta->findCString(kKeyMIMEType, &mime);
- bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
- !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
- bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
- bool is_audio = !strncasecmp(mime, "audio/", 6);
int32_t count = 0;
const int64_t interleaveDurationUs = mOwner->interleaveDuration();
int64_t chunkTimestampUs = 0;
@@ -943,10 +1116,12 @@
int32_t sampleCount = 1; // Sample count in the current stts table entry
uint32_t previousSampleSize = 0; // Size of the previous sample
int64_t previousPausedDurationUs = 0;
+ int64_t timestampUs;
sp<MetaData> meta_data;
bool collectStats = collectStatisticalData();
mNumSamples = 0;
+ mMaxWriteTimeUs = 0;
status_t err = OK;
MediaBuffer *buffer;
while (!mDone && (err = mSource->read(&buffer)) == OK) {
@@ -973,13 +1148,13 @@
&& isCodecConfig) {
CHECK(!mGotAllCodecSpecificData);
- if (is_avc) {
+ if (mIsAvc) {
status_t err = makeAVCCodecSpecificData(
(const uint8_t *)buffer->data()
+ buffer->range_offset(),
buffer->range_length());
CHECK_EQ(OK, err);
- } else if (is_mpeg4) {
+ } else if (mIsMPEG4) {
mCodecSpecificDataSize = buffer->range_length();
mCodecSpecificData = malloc(mCodecSpecificDataSize);
memcpy(mCodecSpecificData,
@@ -994,7 +1169,7 @@
mGotAllCodecSpecificData = true;
continue;
} else if (!mGotAllCodecSpecificData &&
- count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
+ count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) {
// The TI mpeg4 encoder does not properly set the
// codec-specific-data flag.
@@ -1034,7 +1209,7 @@
}
mGotAllCodecSpecificData = true;
- } else if (!mGotAllCodecSpecificData && is_avc && count < 3) {
+ } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) {
// The TI video encoder does not flag codec specific data
// as such and also splits up SPS and PPS across two buffers.
@@ -1090,10 +1265,10 @@
buffer->release();
buffer = NULL;
- if (is_avc) StripStartcode(copy);
+ if (mIsAvc) StripStartcode(copy);
size_t sampleSize;
- sampleSize = is_avc
+ sampleSize = mIsAvc
#if USE_NALLEN_FOUR
? copy->range_length() + 4
#else
@@ -1116,7 +1291,6 @@
int32_t isSync = false;
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
- int64_t timestampUs;
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
////////////////////////////////////////////////////////////////////////////////
@@ -1168,7 +1342,7 @@
trackProgressStatus(timestampUs);
}
if (mOwner->numTracks() == 1) {
- off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy)
+ off_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy)
: mOwner->addSample_l(copy);
if (mChunkOffsets.empty()) {
mChunkOffsets.push_back(offset);
@@ -1182,7 +1356,7 @@
if (interleaveDurationUs == 0) {
StscTableEntry stscEntry(++nChunks, 1, 1);
mStscTableEntries.push_back(stscEntry);
- writeOneChunk(is_avc);
+ bufferChunk(timestampUs);
} else {
if (chunkTimestampUs == 0) {
chunkTimestampUs = timestampUs;
@@ -1199,7 +1373,7 @@
mChunkSamples.size(), 1);
mStscTableEntries.push_back(stscEntry);
}
- writeOneChunk(is_avc);
+ bufferChunk(timestampUs);
chunkTimestampUs = timestampUs;
}
}
@@ -1220,7 +1394,7 @@
++nChunks;
StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1);
mStscTableEntries.push_back(stscEntry);
- writeOneChunk(is_avc);
+ bufferChunk(timestampUs);
}
// We don't really know how long the last frame lasts, since
@@ -1234,10 +1408,10 @@
SttsTableEntry sttsEntry(sampleCount, lastDurationUs);
mSttsTableEntries.push_back(sttsEntry);
mReachedEOS = true;
- LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s",
- count, nZeroLengthFrames, mNumSamples, is_audio? "audio": "video");
+ LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames. Max write time: %lld us - %s",
+ count, nZeroLengthFrames, mNumSamples, mMaxWriteTimeUs, mIsAudio? "audio": "video");
- logStatisticalData(is_audio);
+ logStatisticalData(mIsAudio);
}
void MPEG4Writer::Track::trackProgressStatus(int64_t timeUs, status_t err) {
@@ -1380,24 +1554,17 @@
}
}
-void MPEG4Writer::Track::writeOneChunk(bool isAvc) {
- mOwner->lock();
- for (List<MediaBuffer *>::iterator it = mChunkSamples.begin();
- it != mChunkSamples.end(); ++it) {
- off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it)
- : mOwner->addSample_l(*it);
- if (it == mChunkSamples.begin()) {
- mChunkOffsets.push_back(offset);
- }
- }
- mOwner->unlock();
- while (!mChunkSamples.empty()) {
- List<MediaBuffer *>::iterator it = mChunkSamples.begin();
- (*it)->release();
- (*it) = NULL;
- mChunkSamples.erase(it);
- }
+void MPEG4Writer::Track::bufferChunk(int64_t timestampUs) {
+ LOGV("bufferChunk");
+
+ int64_t startTimeUs = systemTime() / 1000;
+ Chunk chunk(this, timestampUs, mChunkSamples);
+ mOwner->bufferChunk(chunk);
mChunkSamples.clear();
+ int64_t endTimeUs = systemTime() / 1000;
+ if (mMaxWriteTimeUs < endTimeUs - startTimeUs) {
+ mMaxWriteTimeUs = endTimeUs - startTimeUs;
+ }
}
int64_t MPEG4Writer::Track::getDurationUs() const {
@@ -1414,9 +1581,8 @@
bool success = mMeta->findCString(kKeyMIMEType, &mime);
CHECK(success);
- bool is_audio = !strncasecmp(mime, "audio/", 6);
LOGV("%s track time scale: %d",
- is_audio? "Audio": "Video", mTimeScale);
+ mIsAudio? "Audio": "Video", mTimeScale);
time_t now = time(NULL);
@@ -1440,7 +1606,7 @@
mOwner->writeInt32(0); // reserved
mOwner->writeInt16(0); // layer
mOwner->writeInt16(0); // alternate group
- mOwner->writeInt16(is_audio ? 0x100 : 0); // volume
+ mOwner->writeInt16(mIsAudio ? 0x100 : 0); // volume
mOwner->writeInt16(0); // reserved
mOwner->writeInt32(0x10000); // matrix
@@ -1453,7 +1619,7 @@
mOwner->writeInt32(0);
mOwner->writeInt32(0x40000000);
- if (is_audio) {
+ if (mIsAudio) {
mOwner->writeInt32(0);
mOwner->writeInt32(0);
} else {
@@ -1511,16 +1677,16 @@
mOwner->beginBox("hdlr");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(0); // component type: should be mhlr
- mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype
+ mOwner->writeFourcc(mIsAudio ? "soun" : "vide"); // component subtype
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
mOwner->writeInt32(0); // reserved
// Removing "r" for the name string just makes the string 4 byte aligned
- mOwner->writeCString(is_audio ? "SoundHandle": "VideoHandle"); // name
+ mOwner->writeCString(mIsAudio ? "SoundHandle": "VideoHandle"); // name
mOwner->endBox();
mOwner->beginBox("minf");
- if (is_audio) {
+ if (mIsAudio) {
mOwner->beginBox("smhd");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt16(0); // balance
@@ -1553,7 +1719,7 @@
mOwner->beginBox("stsd");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(1); // entry count
- if (is_audio) {
+ if (mIsAudio) {
const char *fourcc = NULL;
if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
fourcc = "samr";
@@ -1735,7 +1901,7 @@
}
mOwner->endBox(); // stts
- if (!is_audio) {
+ if (!mIsAudio) {
mOwner->beginBox("stss");
mOwner->writeInt32(0); // version=0, flags=0
mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames
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 a0ef905..0e3029b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -518,7 +518,7 @@
// Test case 6: Capture the memory usage after every 20 video and audio
// recorded
@LargeTest
- public void testRecordVidedAudioMemoryUsage() throws Exception {
+ public void testRecordVideoAudioMemoryUsage() throws Exception {
boolean memoryResult = false;
mStartPid = getMediaserverPid();
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 6b7020f..ae924cd 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -6,10 +6,11 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- EGL/egl.cpp \
- EGL/hooks.cpp \
- EGL/Loader.cpp \
+LOCAL_SRC_FILES:= \
+ EGL/egl.cpp \
+ EGL/getProcAddress.cpp.arm \
+ EGL/hooks.cpp \
+ EGL/Loader.cpp \
#
LOCAL_SHARED_LIBRARIES += libcutils libutils
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 665446a..315a2a36 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -37,6 +37,8 @@
#include <cutils/memory.h>
#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
#include "hooks.h"
#include "egl_impl.h"
@@ -410,7 +412,11 @@
(__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
-static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS];
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
+
+// accesses protected by gInitDriverMutex
+static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> gGLExtentionMap;
+static int gGLExtentionSlot = 0;
static void(*findProcAddress(const char* name,
const extention_map_t* map, size_t n))()
@@ -1369,55 +1375,54 @@
addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap));
if (addr) return addr;
- return NULL; // TODO: finish implementation below
+ // this protects accesses to gGLExtentionMap and gGLExtentionSlot
+ pthread_mutex_lock(&gInitDriverMutex);
- addr = findProcAddress(procname, gGLExtentionMap, NELEM(gGLExtentionMap));
- if (addr) return addr;
-
- addr = 0;
- int slot = -1;
- for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
- egl_connection_t* const cnx = &gEGLImpl[i];
- if (cnx->dso) {
- if (cnx->egl.eglGetProcAddress) {
- addr = cnx->egl.eglGetProcAddress(procname);
- if (addr) {
- if (slot == -1) {
- slot = 0; // XXX: find free slot
- if (slot == -1) {
- addr = 0;
- break;
- }
- }
- //cnx->hooks->ext.extensions[slot] = addr;
+ /*
+ * Since eglGetProcAddress() is not associated to anything, it needs
+ * to return a function pointer that "works" regardless of what
+ * the current context is.
+ *
+ * For this reason, we return a "forwarder", a small stub that takes
+ * care of calling the function associated with the context
+ * currently bound.
+ *
+ * We first look for extensions we've already resolved, if we're seeing
+ * this extension for the first time, we go through all our
+ * implementations and call eglGetProcAddress() and record the
+ * result in the appropriate implementation hooks and return the
+ * address of the forwarder corresponding to that hook set.
+ *
+ */
+
+ const String8 name(procname);
+ addr = gGLExtentionMap.valueFor(name);
+ const int slot = gGLExtentionSlot;
+
+ LOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
+ "no more slots for eglGetProcAddress(\"%s\")",
+ procname);
+
+ if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
+ bool found = false;
+ for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) {
+ egl_connection_t* const cnx = &gEGLImpl[i];
+ if (cnx->dso && cnx->egl.eglGetProcAddress) {
+ found = true;
+ cnx->hooks[i]->ext.extensions[slot] =
+ cnx->egl.eglGetProcAddress(procname);
}
}
+ if (found) {
+ addr = gExtensionForwarders[slot];
+ gGLExtentionMap.add(name, addr);
+ gGLExtentionSlot++;
+ }
}
- }
-
- if (slot >= 0) {
- addr = 0; // XXX: address of stub 'slot'
- gGLExtentionMap[slot].name = strdup(procname);
- gGLExtentionMap[slot].address = addr;
- }
-
- return addr;
-
- /*
- * TODO: For OpenGL ES extensions, we must generate a stub
- * that looks like
- * mov r12, #0xFFFF0FFF
- * ldr r12, [r12, #-15]
- * ldr r12, [r12, #TLS_SLOT_OPENGL_API*4]
- * mov r12, [r12, #api_offset]
- * ldrne pc, r12
- * mov pc, #unsupported_extension
- *
- * and write the address of the extension in *all*
- * gl_hooks_t::gl_ext_t at offset "api_offset" from gl_hooks_t
- *
- */
+ pthread_mutex_unlock(&gInitDriverMutex);
+
+ return addr;
}
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
new file mode 100644
index 0000000..23837ef
--- /dev/null
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -0,0 +1,176 @@
+/*
+ ** Copyright 2009, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef GL_EXTENSION
+#undef GL_EXTENSION_NAME
+
+#if defined(__arm__)
+
+ #ifdef HAVE_ARM_TLS_REGISTER
+ #define GET_TLS(reg) \
+ "mrc p15, 0, " #reg ", c13, c0, 3 \n"
+ #else
+ #define GET_TLS(reg) \
+ "mov " #reg ", #0xFFFF0FFF \n" \
+ "ldr " #reg ", [" #reg ", #-15] \n"
+ #endif
+
+ #define API_ENTRY(_api) __attribute__((naked)) _api
+
+ #define CALL_GL_EXTENSION_API(_api) \
+ asm volatile( \
+ GET_TLS(r12) \
+ "ldr r12, [r12, %[tls]] \n" \
+ "cmp r12, #0 \n" \
+ "ldrne r12, [r12, %[api]] \n" \
+ "cmpne r12, #0 \n" \
+ "bxne r12 \n" \
+ "bx lr \n" \
+ : \
+ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \
+ [api] "J"(__builtin_offsetof(gl_hooks_t, \
+ ext.extensions[_api])) \
+ : \
+ );
+
+ #define GL_EXTENSION_NAME(_n) __glExtFwd##_n
+
+ #define GL_EXTENSION(_n) \
+ void API_ENTRY(GL_EXTENSION_NAME(_n))() { \
+ CALL_GL_EXTENSION_API(_n); \
+ }
+
+
+#else
+
+ #define GL_EXTENSION_NAME(_n) NULL
+
+ #define GL_EXTENSION(_n)
+
+ #warning "eglGetProcAddress() partially supported on this architecture"
+
+#endif
+
+GL_EXTENSION(0)
+GL_EXTENSION(1)
+GL_EXTENSION(2)
+GL_EXTENSION(3)
+GL_EXTENSION(4)
+GL_EXTENSION(5)
+GL_EXTENSION(6)
+GL_EXTENSION(7)
+GL_EXTENSION(8)
+GL_EXTENSION(9)
+GL_EXTENSION(10)
+GL_EXTENSION(11)
+GL_EXTENSION(12)
+GL_EXTENSION(13)
+GL_EXTENSION(14)
+GL_EXTENSION(15)
+
+GL_EXTENSION(16)
+GL_EXTENSION(17)
+GL_EXTENSION(18)
+GL_EXTENSION(19)
+GL_EXTENSION(20)
+GL_EXTENSION(21)
+GL_EXTENSION(22)
+GL_EXTENSION(23)
+GL_EXTENSION(24)
+GL_EXTENSION(25)
+GL_EXTENSION(26)
+GL_EXTENSION(27)
+GL_EXTENSION(28)
+GL_EXTENSION(29)
+GL_EXTENSION(30)
+GL_EXTENSION(31)
+
+GL_EXTENSION(32)
+GL_EXTENSION(33)
+GL_EXTENSION(34)
+GL_EXTENSION(35)
+GL_EXTENSION(36)
+GL_EXTENSION(37)
+GL_EXTENSION(38)
+GL_EXTENSION(39)
+GL_EXTENSION(40)
+GL_EXTENSION(41)
+GL_EXTENSION(42)
+GL_EXTENSION(43)
+GL_EXTENSION(44)
+GL_EXTENSION(45)
+GL_EXTENSION(46)
+GL_EXTENSION(47)
+
+GL_EXTENSION(48)
+GL_EXTENSION(49)
+GL_EXTENSION(50)
+GL_EXTENSION(51)
+GL_EXTENSION(52)
+GL_EXTENSION(53)
+GL_EXTENSION(54)
+GL_EXTENSION(55)
+GL_EXTENSION(56)
+GL_EXTENSION(57)
+GL_EXTENSION(58)
+GL_EXTENSION(59)
+GL_EXTENSION(60)
+GL_EXTENSION(61)
+GL_EXTENSION(62)
+GL_EXTENSION(63)
+
+extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {
+ GL_EXTENSION_NAME(0), GL_EXTENSION_NAME(1), GL_EXTENSION_NAME(2), GL_EXTENSION_NAME(3),
+ GL_EXTENSION_NAME(4), GL_EXTENSION_NAME(5), GL_EXTENSION_NAME(6), GL_EXTENSION_NAME(7),
+ GL_EXTENSION_NAME(8), GL_EXTENSION_NAME(9), GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11),
+ GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15),
+ GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19),
+ GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23),
+ GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27),
+ GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31),
+ GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35),
+ GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39),
+ GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43),
+ GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47),
+ GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51),
+ GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55),
+ GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59),
+ GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63)
+ };
+
+#undef GL_EXTENSION_NAME
+#undef GL_EXTENSION
+#undef API_ENTRY
+#undef CALL_GL_API
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index f47f093..1ab58cc 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -37,7 +37,7 @@
#endif
#undef NELEM
#define NELEM(x) (sizeof(x)/sizeof(*(x)))
-#define MAX_NUMBER_OF_GL_EXTENSIONS 32
+#define MAX_NUMBER_OF_GL_EXTENSIONS 64
#if defined(HAVE_ANDROID_OS) && !USE_SLOW_BINDING && __OPTIMIZE__
@@ -86,7 +86,7 @@
#include "entries.in"
} gl;
struct gl_ext_t {
- void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void);
+ __eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS];
} ext;
};
#undef GL_ENTRY
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 6eaf0cc..f1c6532 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -125,8 +125,6 @@
metrics.setToDefaults();
PackageParser.PackageLite pkg = packageParser.parsePackageLite(
archiveFilePath, 0);
- ret.packageName = pkg.packageName;
- ret.installLocation = pkg.installLocation;
// Nuke the parser reference right away and force a gc
packageParser = null;
Runtime.getRuntime().gc();
@@ -136,6 +134,7 @@
return ret;
}
ret.packageName = pkg.packageName;
+ ret.installLocation = pkg.installLocation;
ret.recommendedInstallLocation = recommendAppInstallLocation(pkg.installLocation, archiveFilePath, flags);
return ret;
}
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 5615232..cd687da 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -5514,7 +5514,7 @@
throw new SecurityException(
"Injecting to another application requires INJECT_EVENTS permission");
case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
- Slog.v(TAG, "Input event injection succeeded.");
+ //Slog.v(TAG, "Input event injection succeeded.");
return true;
case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
Slog.w(TAG, "Input event injection timed out.");
@@ -7822,8 +7822,8 @@
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
- notifyFocusChanged();
}
} break;
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index a60d2be..03194ff 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -135,9 +135,9 @@
}
// TODO: We don't check for SecurityException here (requires
- // READ_PHONE_STATE permission).
+ // CALL_PRIVILEGED permission).
if (scheme.equals("voicemail")) {
- return TelephonyManager.getDefault().getVoiceMailNumber();
+ return TelephonyManager.getDefault().getCompleteVoiceMailNumber();
}
if (context == null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ab63017..aa916e0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -665,6 +665,25 @@
}
/**
+ * Returns the complete voice mail number. Return null if it is unavailable.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED}
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ try {
+ return getSubscriberInfo().getCompleteVoiceMailNumber();
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
* Returns the voice mail count. Return 0 if unavailable.
* <p>
* Requires Permission:
diff --git a/telephony/java/com/android/internal/telephony/CallManager.java b/telephony/java/com/android/internal/telephony/CallManager.java
new file mode 100644
index 0000000..9822694
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/CallManager.java
@@ -0,0 +1,983 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+
+
+import android.content.Context;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RegistrantList;
+import android.telephony.PhoneStateListener;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ *
+ * CallManager class provides an abstract layer for PhoneApp to access
+ * and control calls. It implements Phone interface.
+ *
+ * CallManager provides call and connection control as well as
+ * channel capability.
+ *
+ * There are three categories of APIs CallManager provided
+ *
+ * 1. Call control and operation, such as dial() and hangup()
+ * 2. Channel capabilities, such as CanConference()
+ * 3. Register notification
+ *
+ *
+ */
+public final class CallManager {
+
+ private static final int EVENT_DISCONNECT = 100;
+ private static final int EVENT_CALL_STATE_CHANGED = 101;
+
+
+ // Singleton instance
+ private static final CallManager INSTANCE = new CallManager();
+
+ // list of registered phones
+ private final ArrayList<Phone> mPhones;
+
+ // list of supported ringing calls
+ private final ArrayList<Call> mRingingCalls;
+
+ // list of supported background calls
+ private final ArrayList<Call> mBackgroundCalls;
+
+ // list of supported foreground calls
+ private final ArrayList<Call> mForegroundCalls;
+
+ // empty connection list
+ private final ArrayList<Connection> emptyConnections = new ArrayList<Connection>();
+
+ // default phone as the first phone registered
+ private Phone mDefaultPhone;
+
+ // state registrants
+ protected final RegistrantList mPreciseCallStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mNewRingingConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mIncomingRingRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mDisconnectRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mServiceStateRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiCompleteRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mMmiRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mUnknownConnectionRegistrants
+ = new RegistrantList();
+
+ protected final RegistrantList mSuppServiceFailedRegistrants
+ = new RegistrantList();
+
+ private CallManager() {
+ mPhones = new ArrayList<Phone>();
+ mRingingCalls = new ArrayList<Call>();
+ mBackgroundCalls = new ArrayList<Call>();
+ mForegroundCalls = new ArrayList<Call>();
+ mDefaultPhone = null;
+ }
+
+ /**
+ * get singleton instance of CallManager
+ * @return CallManager
+ */
+ public static CallManager getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Register phone to CallManager
+ * @param phone
+ * @return
+ */
+ public boolean registerPhone(Phone phone) {
+ if (phone != null && !mPhones.contains(phone)) {
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = phone;
+ }
+ mPhones.add(phone);
+ mRingingCalls.add(phone.getRingingCall());
+ mBackgroundCalls.add(phone.getBackgroundCall());
+ mForegroundCalls.add(phone.getForegroundCall());
+ registerForPhoneStates(phone);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * unregister phone from CallManager
+ * @param phone
+ */
+ public void unregisterPhone(Phone phone) {
+ if (phone != null && !mPhones.contains(phone)) {
+ mPhones.remove(phone);
+ mRingingCalls.remove(phone.getRingingCall());
+ mBackgroundCalls.remove(phone.getBackgroundCall());
+ mForegroundCalls.remove(phone.getForegroundCall());
+ unregisterForPhoneStates(phone);
+ if (phone == mDefaultPhone) {
+ if (mPhones.isEmpty()) {
+ mDefaultPhone = null;
+ } else {
+ mDefaultPhone = mPhones.get(0);
+ }
+ }
+ }
+ }
+
+ private void registerForPhoneStates(Phone phone) {
+ phone.registerForPreciseCallStateChanged(mHandler, EVENT_CALL_STATE_CHANGED, null);
+ phone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
+ }
+
+ private void unregisterForPhoneStates(Phone phone) {
+ phone.unregisterForPreciseCallStateChanged(mHandler);
+ phone.unregisterForDisconnect(mHandler);
+ }
+
+ /**
+ * Answers a ringing or waiting call.
+ *
+ * Active call, if any, go on hold.
+ * If active call can't be held, i.e., a background call of the same channel exists,
+ * the active call will be hang up.
+ *
+ * Answering occurs asynchronously, and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when call is not ringing or waiting
+ */
+ public void acceptCall(Call ringingCall) throws CallStateException {
+ Phone ringingPhone = ringingCall.getPhone();
+
+ if ( hasActiveFgCall() ) {
+ Phone activePhone = getActiveFgCall().getPhone();
+ boolean hasBgCall = activePhone.getBackgroundCall().isIdle();
+ boolean sameChannel = (activePhone == ringingPhone);
+
+ if (sameChannel && hasBgCall) {
+ getActiveFgCall().hangup();
+ } else if (!sameChannel && !hasBgCall) {
+ activePhone.switchHoldingAndActive();
+ } else if (!sameChannel && hasBgCall) {
+ getActiveFgCall().hangup();
+ }
+ }
+
+ ringingPhone.acceptCall();
+ }
+
+ /**
+ * Reject (ignore) a ringing call. In GSM, this means UDUB
+ * (User Determined User Busy). Reject occurs asynchronously,
+ * and final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException when no call is ringing or waiting
+ */
+ public void rejectCall(Call ringingCall) throws CallStateException {
+ Phone ringingPhone = ringingCall.getPhone();
+
+ ringingPhone.rejectCall();
+ }
+
+ /**
+ * Places any active calls on hold, and makes any held calls
+ * active. Switch occurs asynchronously and may fail.
+ * Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if active call is ringing, waiting, or
+ * dialing/alerting, or heldCall can�t be active.
+ * In these cases, this operation may not be performed.
+ */
+ public void switchHoldingAndActive(Call heldCall) throws CallStateException {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ if (activePhone != heldPhone) {
+ activePhone.switchHoldingAndActive();
+ }
+
+ heldPhone.switchHoldingAndActive();
+ }
+
+ /**
+ * Whether or not the phone can conference in the current phone
+ * state--that is, one call holding and one call active.
+ * @return true if the phone can conference; false otherwise.
+ */
+ public boolean canConference(Call heldCall) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ return (heldPhone == activePhone);
+ }
+
+ /**
+ * Conferences holding and active. Conference occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canConference() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ public void conference(Call heldCall) throws CallStateException {
+ if (canConference(heldCall))
+ throw(new CallStateException("Can't conference foreground and selected background call"));
+
+ heldCall.getPhone().conference();
+ }
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @exception CallStateException if a new outgoing call is not currently
+ * possible because no more call slots exist or a call exists that is
+ * dialing, alerting, ringing, or waiting. Other errors are
+ * handled asynchronously.
+ */
+ public Connection dial(Phone phone, String dialString) throws CallStateException {
+ return phone.dial(dialString);
+ }
+
+ /**
+ * Initiate a new voice connection. This happens asynchronously, so you
+ * cannot assume the audio path is connected (or a call index has been
+ * assigned) until PhoneStateChanged notification has occurred.
+ *
+ * @exception CallStateException if a new outgoing call is not currently
+ * possible because no more call slots exist or a call exists that is
+ * dialing, alerting, ringing, or waiting. Other errors are
+ * handled asynchronously.
+ */
+ public Connection dial(Phone phone, String dialString, UUSInfo uusInfo) throws CallStateException {
+ return phone.dial(dialString, uusInfo);
+ }
+
+ /**
+ * clear disconnect connection for each phone
+ */
+ public void clearDisconnected() {
+ for(Phone phone : mPhones) {
+ phone.clearDisconnected();
+ }
+ }
+
+ /**
+ * Whether or not the phone can do explicit call transfer in the current
+ * phone state--that is, one call holding and one call active.
+ * @return true if the phone can do explicit call transfer; false otherwise.
+ */
+ public boolean canTransfer(Call heldCall) {
+ Phone activePhone = null;
+ Phone heldPhone = null;
+
+ if (hasActiveFgCall()) {
+ activePhone = getActiveFgCall().getPhone();
+ }
+
+ if (heldCall != null) {
+ heldPhone = heldCall.getPhone();
+ }
+
+ return (heldPhone == activePhone && activePhone.canTransfer());
+ }
+
+ /**
+ * Connects the held call and active call
+ * Disconnects the subscriber from both calls
+ *
+ * Explicit Call Transfer occurs asynchronously
+ * and may fail. Final notification occurs via
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
+ *
+ * @exception CallStateException if canTransfer() would return false.
+ * In these cases, this operation may not be performed.
+ */
+ public void explicitCallTransfer(Call heldCall) throws CallStateException {
+ if (canTransfer(heldCall)) {
+ heldCall.getPhone().explicitCallTransfer();
+ }
+ }
+
+ /**
+ * Returns a list of MMI codes that are pending for a phone. (They have initiated
+ * but have not yet completed).
+ * Presently there is only ever one.
+ *
+ * Use <code>registerForMmiInitiate</code>
+ * and <code>registerForMmiComplete</code> for change notification.
+ * @return null if phone doesn't have or support mmi code
+ */
+ public List<? extends MmiCode> getPendingMmiCodes(Phone phone) {
+ return null;
+ }
+
+ /**
+ * Sends user response to a USSD REQUEST message. An MmiCode instance
+ * representing this response is sent to handlers registered with
+ * registerForMmiInitiate.
+ *
+ * @param ussdMessge Message to send in the response.
+ * @return false if phone doesn't support ussd service
+ */
+ public boolean sendUssdResponse(Phone phone, String ussdMessge) {
+ return false;
+ }
+
+ /**
+ * Mutes or unmutes the microphone for the active call. The microphone
+ * is automatically unmuted if a call is answered, dialed, or resumed
+ * from a holding state.
+ *
+ * @param muted true to mute the microphone,
+ * false to activate the microphone.
+ */
+
+ public void setMute(boolean muted) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().setMute(muted);
+ }
+ }
+
+ /**
+ * Gets current mute status. Use
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}
+ * as a change notifcation, although presently phone state changed is not
+ * fired when setMute() is called.
+ *
+ * @return true is muting, false is unmuting
+ */
+ public boolean getMute() {
+ if (hasActiveFgCall()) {
+ return getActiveFgCall().getPhone().getMute();
+ }
+ return false;
+ }
+
+ /**
+ * Play a DTMF tone on the active call.
+ *
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ * @return false if no active call or the active call doesn't support
+ * dtmf tone
+ */
+ public boolean sendDtmf(char c) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendDtmf(c);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Start to paly a DTMF tone on the active call.
+ * or there is a playing DTMF tone.
+ * @param c should be one of 0-9, '*' or '#'. Other values will be
+ * silently ignored.
+ *
+ * @return false if no active call or the active call doesn't support
+ * dtmf tone
+ */
+ public boolean startDtmf(char c) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendDtmf(c);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stop the playing DTMF tone. Ignored if there is no playing DTMF
+ * tone or no active call.
+ */
+ public void stopDtmf(Phone phone) {
+ phone.stopDtmf();
+ }
+
+ /**
+ * 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 on the DTMF ON length in milliseconds, or 0 for default
+ * @param off the DTMF OFF length in milliseconds, or 0 for default
+ * @param onComplete is the callback message when the action is processed by BP
+ *
+ */
+ public boolean sendBurstDtmf(Phone phone, String dtmfString, int on, int off, Message onComplete) {
+ if (hasActiveFgCall()) {
+ getActiveFgCall().getPhone().sendBurstDtmf(dtmfString, on, off, onComplete);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Notifies when a voice connection has disconnected, either due to local
+ * or remote hangup or error.
+ *
+ * Messages received from this will have the following members:<p>
+ * <ul><li>Message.obj will be an AsyncResult</li>
+ * <li>AsyncResult.userObj = obj</li>
+ * <li>AsyncResult.result = a Connection object that is
+ * no longer connected.</li></ul>
+ */
+ public void registerForDisconnect(Handler h, int what, Object obj) {
+ mDisconnectRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice disconnection notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForDisconnect(Handler h){
+ mDisconnectRegistrants.remove(h);
+ }
+
+ /**
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
+ * Resulting events will have an AsyncResult in <code>Message.obj</code>.
+ * AsyncResult.userData will be set to the obj argument here.
+ * The <em>h</em> parameter is held only by a weak reference.
+ */
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
+ }
+
+ /**
+ * Unregisters for voice call state change notifications.
+ * Extraneous calls are tolerated silently.
+ */
+ public void unregisterForPreciseCallStateChanged(Handler h){
+ mPreciseCallStateRegistrants.remove(h);
+ }
+
+ /**
+ * Notifies when a previously untracked non-ringing/waiting connection has appeared.
+ * This is likely due to some other entity (eg, SIM card application) initiating a call.
+ */
+ public void registerForUnknownConnection(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for unknown connection notifications.
+ */
+ public void unregisterForUnknownConnection(Handler h){}
+
+
+ /**
+ * Notifies when a new ringing or waiting connection has appeared.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection. <p>
+ * Please check Connection.isRinging() to make sure the Connection
+ * has not dropped since this message was posted.
+ * If Connection.isRinging() is true, then
+ * Connection.getCall() == Phone.getRingingCall()
+ */
+ public void registerForNewRingingConnection(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for new ringing connection notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForNewRingingConnection(Handler h){}
+
+ /**
+ * Notifies when an incoming call rings.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = a Connection. <p>
+ */
+ public void registerForIncomingRing(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for ring notification.
+ * Extraneous calls are tolerated silently
+ */
+
+ public void unregisterForIncomingRing(Handler h){}
+
+ /**
+ * Notifies when out-band ringback tone is needed.<p>
+ *
+ * Messages received from this:
+ * Message.obj will be an AsyncResult
+ * AsyncResult.userObj = obj
+ * AsyncResult.result = boolean, true to start play ringback tone
+ * and false to stop. <p>
+ */
+ public void registerForRingbackTone(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for ringback tone notification.
+ */
+
+ public void unregisterForRingbackTone(Handler h){}
+
+ /**
+ * Registers the handler to reset the uplink mute state to get
+ * uplink audio.
+ */
+ public void registerForResendIncallMute(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for resend incall mute notifications.
+ */
+ public void unregisterForResendIncallMute(Handler h){}
+
+
+
+ /**
+ * Register for notifications of initiation of a new MMI code request.
+ * MMI codes for GSM are discussed in 3GPP TS 22.030.<p>
+ *
+ * Example: If Phone.dial is called with "*#31#", then the app will
+ * be notified here.<p>
+ *
+ * The returned <code>Message.obj</code> will contain an AsyncResult.
+ *
+ * <code>obj.result</code> will be an "MmiCode" object.
+ */
+ public void registerForMmiInitiate(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for new MMI initiate notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiInitiate(Handler h){}
+
+ /**
+ * Register for notifications that an MMI request has completed
+ * its network activity and is in its final state. This may mean a state
+ * of COMPLETE, FAILED, or CANCELLED.
+ *
+ * <code>Message.obj</code> will contain an AsyncResult.
+ * <code>obj.result</code> will be an "MmiCode" object
+ */
+ public void registerForMmiComplete(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for MMI complete notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForMmiComplete(Handler h){}
+
+ /**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what user-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj){}
+
+ /**
+ * Unregister for notification for Ecm timer reset
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEcmTimerReset(Handler h){}
+
+
+
+ /**
+ * Register for ServiceState changed.
+ * Message.obj will contain an AsyncResult.
+ * AsyncResult.result will be a ServiceState instance
+ */
+ public void registerForServiceStateChanged(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for ServiceStateChange notification.
+ * Extraneous calls are tolerated silently
+ */
+ public void unregisterForServiceStateChanged(Handler h){}
+
+ /**
+ * Register for Supplementary Service 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.
+ */
+ public void registerForSuppServiceNotification(Handler h, int what, Object obj){}
+
+ /**
+ * Unregisters for Supplementary Service notifications.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSuppServiceNotification(Handler h){}
+
+ /**
+ * Register for notifications when a supplementary service attempt fails.
+ * Message.obj will contain an AsyncResult.
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForSuppServiceFailed(Handler h, int what, Object obj){}
+
+ /**
+ * Unregister for notifications when a supplementary service attempt fails.
+ * Extraneous calls are tolerated silently
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSuppServiceFailed(Handler h){}
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){}
+
+ /**
+ * Unegister for notifications when a sInCall VoicePrivacy is enabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOn(Handler h){}
+
+ /**
+ * Register for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler that receives the notification message.
+ * @param what User-defined message code.
+ * @param obj User object.
+ */
+ public void registerForInCallVoicePrivacyOff(Handler h, int what, Object obj){}
+
+ /**
+ * Unegister for notifications when a sInCall VoicePrivacy is disabled
+ *
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForInCallVoicePrivacyOff(Handler h){}
+
+ /**
+ * 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.
+ */
+ public void registerForCdmaOtaStatusChange(Handler h, int what, Object obj){}
+
+ /**
+ * Unegister for notifications when CDMA OTA Provision status change
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForCdmaOtaStatusChange(Handler h){}
+
+ /**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj){}
+
+ /**
+ * Unregister for notifications for subscription info
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSubscriptionInfoReady(Handler h){}
+
+ /* APIs to access foregroudCalls, backgroudCalls, and ringingCalls
+ * 1. APIs to access list of calls
+ * 2. APIs to check if any active call, which has connection other than
+ * disconnected ones, pleaser refer to Call.isIdle()
+ * 3. APIs to return first active call
+ * 4. APIs to return the connections of first active call
+ * 5. APIs to return other property of first active call
+ */
+
+ /**
+ * @return list of ringing calls
+ */
+ public ArrayList<Call> getRingingCalls() {
+ return mBackgroundCalls;
+ }
+
+ /**
+ * @return list of background calls
+ */
+ public ArrayList<Call> getBackgroundCalls() {
+ return mBackgroundCalls;
+ }
+
+ /**
+ * Return true if there is at least one active foreground call
+ */
+ public boolean hasActiveFgCall() {
+ return (getFirstActiveCall(mForegroundCalls) != null);
+ }
+
+ /**
+ * Return true if there is at least one active background call
+ */
+ public boolean hasActiveBgCall() {
+ // TODO since hasActiveBgCall may get called often
+ // better to cache it to improve performance
+ return (getFirstActiveCall(mBackgroundCalls) != null);
+ }
+
+ /**
+ * Return true if there is at least one active ringing call
+ *
+ */
+ public boolean hasActiveRingingCall() {
+ return (getFirstActiveCall(mRingingCalls) != null);
+ }
+
+ /**
+ * return the active foreground call from foreground calls
+ *
+ * Active call means the call is NOT in Call.State.IDLE
+ *
+ * 1. If there is active foreground call, return it
+ * 2. If there is no active foreground call, return the
+ * foreground call associated with default phone, which state is IDLE.
+ * 3. If there is no phone registered at all, return null.
+ *
+ */
+ public Call getActiveFgCall() {
+ for (Call call : mForegroundCalls) {
+ if (call.getState() != Call.State.IDLE) {
+ return call;
+ }
+ }
+ return (mDefaultPhone == null) ?
+ null : mDefaultPhone.getForegroundCall();
+ }
+
+ /**
+ * return one active background call from background calls
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active background call, return it
+ * 2. If there is more than one active background call, return the first one
+ * 3. If there is no active background call, return the background call
+ * associated with default phone, which state is IDLE.
+ * 4. If there is no background call at all, return null.
+ *
+ * Complete background calls list can be get by getBackgroundCalls()
+ */
+ public Call getFirstActiveBgCall() {
+ for (Call call : mBackgroundCalls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return (mDefaultPhone == null) ?
+ null : mDefaultPhone.getBackgroundCall();
+ }
+
+ /**
+ * return one active ringing call from ringing calls
+ *
+ * Active call means the call is NOT idle defined by Call.isIdle()
+ *
+ * 1. If there is only one active ringing call, return it
+ * 2. If there is more than one active ringing call, return the first one
+ * 3. If there is no active ringing call, return the ringing call
+ * associated with default phone, which state is IDLE.
+ * 4. If there is no ringing call at all, return null.
+ *
+ * Complete ringing calls list can be get by getRingingCalls()
+ */
+ public Call getFirstActiveRingingCall() {
+ for (Call call : mRingingCalls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return (mDefaultPhone == null) ?
+ null : mDefaultPhone.getRingingCall();
+ }
+
+ /**
+ * @return the state of active foreground call
+ * return IDLE if there is no active foreground call
+ */
+ public Call.State getActiveFgCallState() {
+ Call fgCall = getActiveFgCall();
+
+ if (fgCall != null) {
+ return fgCall.getState();
+ }
+
+ return Call.State.IDLE;
+ }
+
+ /**
+ * @return the connections of active foreground call
+ * return null if there is no active foreground call
+ */
+ public List<Connection> getFgCallConnections() {
+ Call fgCall = getActiveFgCall();
+ if ( fgCall != null) {
+ return fgCall.getConnections();
+ }
+ return emptyConnections;
+ }
+
+ /**
+ * @return the connections of active background call
+ * return empty list if there is no active background call
+ */
+ public List<Connection> getBgCallConnections() {
+ Call bgCall = getActiveFgCall();
+ if ( bgCall != null) {
+ return bgCall.getConnections();
+ }
+ return emptyConnections;
+ }
+
+ /**
+ * @return the latest connection of active foreground call
+ * return null if there is no active foreground call
+ */
+ public Connection getFgCallLatestConnection() {
+ Call fgCall = getActiveFgCall();
+ if ( fgCall != null) {
+ return fgCall.getLatestConnection();
+ }
+ return null;
+ }
+
+ /**
+ * @return true if there is at least one Foreground call in disconnected state
+ */
+ public boolean hasDisconnectedFgCall() {
+ return (getFirstCallOfState(mForegroundCalls, Call.State.DISCONNECTED) != null);
+ }
+
+ /**
+ * @return true if there is at least one background call in disconnected state
+ */
+ public boolean hasDisconnectedBgCall() {
+ return (getFirstCallOfState(mBackgroundCalls, Call.State.DISCONNECTED) != null);
+ }
+
+ /**
+ * @return the first active call from a call list
+ */
+ private Call getFirstActiveCall(ArrayList<Call> calls) {
+ for (Call call : calls) {
+ if (!call.isIdle()) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the first call in a the Call.state from a call list
+ */
+ private Call getFirstCallOfState(ArrayList<Call> calls, Call.State state) {
+ for (Call call : calls) {
+ if (call.getState() == state) {
+ return call;
+ }
+ }
+ return null;
+ }
+
+
+
+
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_DISCONNECT:
+ mDisconnectRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ case EVENT_CALL_STATE_CHANGED:
+ mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
+ break;
+ }
+ }
+ };
+}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index e74b9e4..5cba2e1 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -59,6 +59,11 @@
String getVoiceMailNumber();
/**
+ * Retrieves the complete voice mail number.
+ */
+ String getCompleteVoiceMailNumber();
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
String getVoiceMailAlphaTag();
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 4f71bb1..86c86bb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.telephony.PhoneNumberUtils;
import android.util.Log;
public class PhoneSubInfo extends IPhoneSubInfo.Stub {
@@ -29,6 +30,9 @@
private Context mContext;
private static final String READ_PHONE_STATE =
android.Manifest.permission.READ_PHONE_STATE;
+ private static final String CALL_PRIVILEGED =
+ // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE
+ android.Manifest.permission.CALL_PRIVILEGED;
public PhoneSubInfo(Phone phone) {
mPhone = phone;
@@ -101,7 +105,22 @@
*/
public String getVoiceMailNumber() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
- return (String) mPhone.getVoiceMailNumber();
+ String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number);
+ return number;
+ }
+
+ /**
+ * Retrieves the compelete voice mail number.
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
+ "Requires CALL_PRIVILEGED");
+ String number = mPhone.getVoiceMailNumber();
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number);
+ return number;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index 202ded2..7009893 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -82,6 +82,13 @@
}
/**
+ * Retrieves the complete voice mail number.
+ */
+ public String getCompleteVoiceMailNumber() {
+ return mPhoneSubInfo.getCompleteVoiceMailNumber();
+ }
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
public String getVoiceMailAlphaTag() {