diff options
15 files changed, 1173 insertions, 87 deletions
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 @@ public class PackageParser { * 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 final class Settings { 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/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 @@ Android Market within a 14-day period ending on the data collection date noted b <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 @@ Android%201.5|Android%201.6|Android%202.1|Android%202.2&chco=c4df9b,6fad0c" /> <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 @@ Android Market within a 14-day period ending on the date indicated on the x-axis <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" /> - -<p><em>Last historical dataset collected during two weeks ending on July 1, 2010</em></p> +<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 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 @@ ending on the data collection date noted below.</p> <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 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" /> <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 @@ private: 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/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 @@ AudioSource::AudioSource( 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 @@ status_t AudioSource::stop() { 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 @@ sp<MetaData> AudioSource::getFormat() { 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; } + mPrevSampleTimeUs = mStartTimeUs; } - 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; + uint32_t sampleRate = mRecord->getSampleRate(); + + // 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; } - } else { - n = mRecord->read(buffer->data(), buffer->size()); + 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(); - buffer = NULL; - return (status_t)n; } - 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); - buffer->release(); - return UNKNOWN_ERROR; - } - LOGV("skipFrame: %lld us > timestamp: %lld us, samples %d", - skipFrameUs, timestampUs, numFramesRecorded); + 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 @@ status_t AudioSource::read( 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/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 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi // 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/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 @@ public class DefaultContainerService extends IntentService { 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 @@ public class DefaultContainerService extends IntentService { 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..4940311 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5514,7 +5514,7 @@ public class WindowManagerService extends IWindowManager.Stub 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."); 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 @@ public class PhoneNumberUtils } // 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 @@ public class TelephonyManager { } /** + * 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 @@ interface IPhoneSubInfo { 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 java.io.PrintWriter; 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 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub { 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 class PhoneSubInfo extends IPhoneSubInfo.Stub { */ 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 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub { } /** + * 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() { |
