diff options
| -rw-r--r-- | api/current.xml | 59 | ||||
| -rw-r--r-- | include/media/ToneGenerator.h | 89 | ||||
| -rw-r--r-- | location/java/com/android/internal/location/GpsLocationProvider.java | 60 | ||||
| -rw-r--r-- | media/java/android/media/AudioManager.java | 77 | ||||
| -rw-r--r-- | media/java/android/media/AudioService.java | 176 | ||||
| -rw-r--r-- | media/java/android/media/ToneGenerator.java | 66 | ||||
| -rw-r--r-- | media/libmedia/ToneGenerator.cpp | 427 | ||||
| -rw-r--r-- | media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java | 14 |
8 files changed, 804 insertions, 164 deletions
diff --git a/api/current.xml b/api/current.xml index 2bab739d38d7..509abe1b782c 100644 --- a/api/current.xml +++ b/api/current.xml @@ -63668,7 +63668,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="mode" type="int"> @@ -63879,7 +63879,7 @@ synchronized="false" static="false" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="mode" type="int"> @@ -68595,6 +68595,17 @@ visibility="public" > </field> +<field name="TONE_SUP_CONFIRM" + type="int" + transient="false" + volatile="false" + value="32" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TONE_SUP_CONGESTION" type="int" transient="false" @@ -68606,6 +68617,17 @@ visibility="public" > </field> +<field name="TONE_SUP_CONGESTION_ABBREV" + type="int" + transient="false" + volatile="false" + value="31" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TONE_SUP_DIAL" type="int" transient="false" @@ -68628,6 +68650,39 @@ visibility="public" > </field> +<field name="TONE_SUP_INTERCEPT" + type="int" + transient="false" + volatile="false" + value="29" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_SUP_INTERCEPT_ABBREV" + type="int" + transient="false" + volatile="false" + value="30" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="TONE_SUP_PIP" + type="int" + transient="false" + volatile="false" + value="33" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TONE_SUP_RADIO_ACK" type="int" transient="false" diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h index ec64e4d70434..6b0cc8ab8cce 100644 --- a/include/media/ToneGenerator.h +++ b/include/media/ToneGenerator.h @@ -18,7 +18,7 @@ #define ANDROID_TONEGENERATOR_H_ #include <utils/RefBase.h> -#include <utils/Vector.h> +#include <utils/KeyedVector.h> #include <utils/threads.h> #include <media/AudioSystem.h> #include <media/AudioTrack.h> @@ -49,21 +49,30 @@ public: TONE_DTMF_C, // C key: 1633Hz, 852Hz TONE_DTMF_D, // D key: 1633Hz, 941Hz // Call supervisory tones: 3GPP TS 22.001 (CEPT) - TONE_SUP_DIAL, // Dial tone: 425Hz, continuous - TONE_SUP_BUSY, // Busy tone: 425Hz, 500ms ON, 500ms OFF... - TONE_SUP_CONGESTION, // Congestion tone: 425Hz, 200ms ON, 200ms OFF... - TONE_SUP_RADIO_ACK, // Radio path acknowlegment: 425Hz, 200ms ON + TONE_SUP_DIAL, // Dial tone: CEPT: 425Hz, continuous + FIRST_SUP_TONE = TONE_SUP_DIAL, + TONE_SUP_BUSY, // Busy tone, CEPT: 425Hz, 500ms ON, 500ms OFF... + TONE_SUP_CONGESTION, // Congestion tone CEPT, JAPAN: 425Hz, 200ms ON, 200ms OFF... + TONE_SUP_RADIO_ACK, // Radio path acknowlegment, CEPT, ANSI: 425Hz, 200ms ON TONE_SUP_RADIO_NOTAVAIL, // Radio path not available: 425Hz, 200ms ON, 200 OFF 3 bursts TONE_SUP_ERROR, // Error/Special info: 950Hz+1400Hz+1800Hz, 330ms ON, 1s OFF... - TONE_SUP_CALL_WAITING, // Call Waiting: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... - TONE_SUP_RINGTONE, // Ring Tone: 425Hz, 1s ON, 4s OFF... + TONE_SUP_CALL_WAITING, // Call Waiting CEPT,JAPAN: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... + TONE_SUP_RINGTONE, // Ring Tone CEPT, JAPAN: 425Hz, 1s ON, 4s OFF... + LAST_SUP_TONE = TONE_SUP_RINGTONE, // Proprietary tones: 3GPP TS 31.111 TONE_PROP_BEEP, // General beep: 400Hz+1200Hz, 35ms ON TONE_PROP_ACK, // Positive Acknowlgement: 1200Hz, 100ms ON, 100ms OFF 2 bursts - TONE_PROP_NACK, // Negative Acknowlgement: 300Hz+400Hz+500Hz, 400ms ON + TONE_PROP_NACK, // Negative Acknowlgement: 300Hz+400Hz+500Hz, 400ms ON TONE_PROP_PROMPT, // Prompt tone: 400Hz+1200Hz, 200ms ON TONE_PROP_BEEP2, // General double beep: 400Hz+1200Hz, 35ms ON, 200ms OFF, 35ms on - NUM_TONES + // Additional call supervisory tones: specified by IS-95 only + TONE_SUP_INTERCEPT, // Intercept tone: alternating 440 Hz and 620 Hz tones, each on for 250 ms. + TONE_SUP_INTERCEPT_ABBREV, // Abbreviated intercept: intercept tone limited to 4 seconds + TONE_SUP_CONGESTION_ABBREV, // Abbreviated congestion: congestion tone limited to 4 seconds + TONE_SUP_CONFIRM, // Confirm tone: a 350 Hz tone added to a 440 Hz tone repeated 3 times in a 100 ms on, 100 ms off cycle. + TONE_SUP_PIP, // Pip tone: four bursts of 480 Hz tone (0.1 s on, 0.1 s off). + NUM_TONES, + NUM_SUP_TONES = LAST_SUP_TONE-FIRST_SUP_TONE+1 }; ToneGenerator(int streamType, float volume); @@ -85,13 +94,45 @@ private: TONE_RESTARTING // }; - static const unsigned int TONEGEN_MAX_WAVES = 3; - static const unsigned int TONEGEN_MAX_SEGMENTS = 4; // Maximun number of elenemts in + + // Region specific tones. + // These supervisory tones are different depending on the region (USA/CANADA, JAPAN, rest of the world). + // When a tone in the range [FIRST_SUP_TONE, LAST_SUP_TONE] is requested, the region is determined + // from system property gsm.operator.iso-country and the proper tone descriptor is selected with the + // help of sToneMappingTable[] + enum regional_tone_type { + // ANSI supervisory tones + TONE_ANSI_DIAL = NUM_TONES, // Dial tone: a continuous 350 Hz + 440 Hz tone. + TONE_ANSI_BUSY, // Busy tone on: a 480 Hz + 620 Hz tone repeated in a 500 ms on, 500 ms off cycle. + TONE_ANSI_CONGESTION, // Network congestion (reorder) tone on: a 480 Hz + 620 Hz tone repeated in a 250 ms on, 250 ms off cycle. + TONE_ANSI_CALL_WAITING, // Call waiting tone on: 440 Hz, on for 300 ms, 9,7 s off followed by + // (440 Hz, on for 100 ms off for 100 ms, on for 100 ms, 9,7s off and repeated as necessary). + TONE_ANSI_RINGTONE, // Ring Tone: a 440 Hz + 480 Hz tone repeated in a 2 s on, 4 s off pattern. + // JAPAN Supervisory tones + TONE_JAPAN_DIAL, // Dial tone: 400Hz, continuous + TONE_JAPAN_BUSY, // Busy tone: 400Hz, 500ms ON, 500ms OFF... + TONE_JAPAN_RADIO_ACK, // Radio path acknowlegment: 400Hz, 1s ON, 2s OFF... + NUM_ALTERNATE_TONES + }; + + enum region { + ANSI, + JAPAN, + CEPT, + NUM_REGIONS + }; + + static const unsigned char sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES]; + + static const unsigned int TONEGEN_MAX_WAVES = 3; // Maximun number of sine waves in a tone segment + static const unsigned int TONEGEN_MAX_SEGMENTS = 5; // Maximun number of segments in a tone descriptor static const unsigned int TONEGEN_INF = 0xFFFFFFFF; // Represents infinite time duration static const float TONEGEN_GAIN = 0.9; // Default gain passed to WaveGenerator(). // ToneDescriptor class contains all parameters needed to generate a tone: - // - The array waveFreq[] contains the frequencies of all individual waves making the multi-tone. + // - The array waveFreq[]: + // 1 for static tone descriptors: contains the frequencies of all individual waves making the multi-tone. + // 2 for active tone descritors: contains the indexes of the WaveGenerator objects in mWaveGens // The number of sine waves varies from 1 to TONEGEN_MAX_WAVES. // The first null value indicates that no more waves are needed. // - The array segments[] is used to generate the tone pulses. A segment is a period of time @@ -100,17 +141,25 @@ private: // The data stored in segments[] is the duration of the corresponding period in ms. // The first segment encountered with a 0 duration indicates that no more segment follows. // - repeatCnt indicates the number of times the sequence described by segments[] array must be repeated. - // When the tone generator encounters the first 0 duration segment, it will compare repeatCnt to mCurCount. - // If mCurCount > repeatCnt, the tone is stopped automatically. + // When the tone generator encounters the first 0 duration segment, it will compare repeatCnt to mCurCount. + // If mCurCount > repeatCnt, the tone is stopped automatically. Otherwise, tone sequence will be + // restarted from segment repeatSegment. + // - repeatSegment number of the first repeated segment when repeatCnt is not null - class ToneDescriptor { + class ToneSegment { public: + unsigned int duration; unsigned short waveFreq[TONEGEN_MAX_WAVES+1]; - unsigned long segments[TONEGEN_MAX_SEGMENTS+1]; + }; + + class ToneDescriptor { + public: + ToneSegment segments[TONEGEN_MAX_SEGMENTS+1]; unsigned long repeatCnt; + unsigned long repeatSegment; }; - static const ToneDescriptor toneDescriptors[NUM_TONES]; + static const ToneDescriptor sToneDescriptors[]; unsigned int mTotalSmp; // Total number of audio samples played (gives current time) unsigned int mNextSegSmp; // Position of next segment transition expressed in samples @@ -121,6 +170,7 @@ private: unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[] unsigned short mCurCount; // Current sequence repeat count volatile unsigned short mState; // ToneGenerator state (tone_state) + unsigned short mRegion; const ToneDescriptor *mpToneDesc; // pointer to active tone descriptor const ToneDescriptor *mpNewToneDesc; // pointer to next active tone descriptor @@ -136,8 +186,9 @@ private: bool initAudioTrack(); static void audioCallback(int event, void* user, void *info); bool prepareWave(); - unsigned int numWaves(); + unsigned int numWaves(unsigned int segmentIdx); void clearWaveGens(); + int getToneForRegion(int toneType); // WaveGenerator generates a single sine wave class WaveGenerator { @@ -167,7 +218,7 @@ private: short mAmplitude_Q15; // Q15 amplitude }; - Vector<WaveGenerator *> mWaveGens; // list of active wave generators. + KeyedVector<unsigned short, WaveGenerator *> mWaveGens; // list of active wave generators. }; } diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 5877dd115899..97b6a620d302 100644 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -16,6 +16,8 @@ package com.android.internal.location; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -59,6 +61,9 @@ import java.util.Properties; public class GpsLocationProvider extends ILocationProvider.Stub { private static final String TAG = "GpsLocationProvider"; + + private static final boolean DEBUG = true; + private static final boolean VERBOSE = false; /** * Broadcast intent action indicating that the GPS has either been @@ -151,6 +156,9 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // turn off GPS fix icon if we haven't received a fix in 10 seconds private static final long RECENT_FIX_TIMEOUT = 10 * 1000; + + // number of fixes to receive before disabling GPS + private static final int MIN_FIX_COUNT = 10; // true if we are enabled private boolean mEnabled; @@ -164,6 +172,9 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // requested frequency of fixes, in seconds private int mFixInterval = 1; + // number of fixes we have received since we started navigating + private int mFixCount; + private int mPositionMode = GPS_POSITION_MODE_STANDALONE; // true if we started navigation @@ -196,6 +207,11 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private int mSuplDataConnectionState; private final ConnectivityManager mConnMgr; + // Alarms + private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP"; + private final AlarmManager mAlarmManager; + private final PendingIntent mWakeupIntent; + private final IBatteryStats mBatteryStats; private final SparseIntArray mClientUids = new SparseIntArray(); @@ -257,11 +273,14 @@ public class GpsLocationProvider extends ILocationProvider.Stub { return mGpsStatusProvider; } - private class TelephonyBroadcastReceiver extends BroadcastReceiver { + private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { + if (action.equals(ALARM_WAKEUP)) { + if (DEBUG) Log.d(TAG, "ALARM_WAKEUP"); + startNavigating(); + } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { String state = intent.getStringExtra(Phone.STATE_KEY); String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); @@ -278,7 +297,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } } - } + }; public static boolean isSupported() { return native_is_supported(); @@ -288,10 +307,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub { mContext = context; mLocationManager = locationManager; - TelephonyBroadcastReceiver receiver = new TelephonyBroadcastReceiver(); + mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ALARM_WAKEUP); intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); - context.registerReceiver(receiver, intentFilter); + context.registerReceiver(mBroadcastReciever, intentFilter); mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -512,11 +534,11 @@ public class GpsLocationProvider extends ILocationProvider.Stub { public void enableLocationTracking(boolean enable) { if (enable) { - mFixRequestTime = System.currentTimeMillis(); mTTFF = 0; mLastFixTime = 0; startNavigating(); } else { + mAlarmManager.cancel(mWakeupIntent); stopNavigating(); } } @@ -622,7 +644,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { public void startNavigating() { if (!mStarted) { - if (Config.LOGV) Log.v(TAG, "startNavigating"); + if (DEBUG) Log.d(TAG, "startNavigating"); mStarted = true; if (!native_start(mPositionMode, false, mFixInterval)) { mStarted = false; @@ -631,11 +653,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // reset SV count to zero updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0); + mFixCount = 0; + mFixRequestTime = System.currentTimeMillis(); } } public void stopNavigating() { - if (Config.LOGV) Log.v(TAG, "stopNavigating"); + if (DEBUG) Log.d(TAG, "stopNavigating"); if (mStarted) { mStarted = false; native_stop(); @@ -653,7 +677,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { */ private void reportLocation(int flags, double latitude, double longitude, double altitude, float speed, float bearing, float accuracy, long timestamp) { - if (Config.LOGV) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude + + if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude + " timestamp: " + timestamp); mLastFixTime = System.currentTimeMillis(); @@ -721,13 +745,23 @@ public class GpsLocationProvider extends ILocationProvider.Stub { mContext.sendBroadcast(intent); updateStatus(LocationProvider.AVAILABLE, mSvCount); } + + if (mFixCount++ >= MIN_FIX_COUNT && mFixInterval > 1) { + if (DEBUG) Log.d(TAG, "exceeded MIN_FIX_COUNT"); + stopNavigating(); + mFixCount = 0; + mAlarmManager.cancel(mWakeupIntent); + long now = SystemClock.elapsedRealtime(); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + mFixInterval * 1000, mWakeupIntent); + } } /** * called from native code to update our status */ private void reportStatus(int status) { - if (Config.LOGV) Log.v(TAG, "reportStatus status: " + status); + if (VERBOSE) Log.v(TAG, "reportStatus status: " + status); boolean wasNavigating = mNavigating; mNavigating = (status == GPS_STATUS_SESSION_BEGIN); @@ -797,12 +831,12 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - if (Config.LOGD) { - if (Config.LOGV) Log.v(TAG, "SV count: " + svCount + + if (VERBOSE) { + Log.v(TAG, "SV count: " + svCount + " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) + " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK])); for (int i = 0; i < svCount; i++) { - if (Config.LOGV) Log.v(TAG, "sv: " + mSvs[i] + + Log.v(TAG, "sv: " + mSvs[i] + " snr: " + (float)mSnrs[i]/10 + " elev: " + mSvElevations[i] + " azimuth: " + mSvAzimuths[i] + diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index f509fb5abf26..e43c9c4386ea 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -642,7 +642,9 @@ public class AudioManager { * <var>false</var> to turn it off */ public void setSpeakerphoneOn(boolean on){ - setRouting(MODE_IN_CALL, on ? ROUTE_SPEAKER : ROUTE_EARPIECE, ROUTE_ALL); + // Temporary fix for issue #1713090 until audio routing is refactored in eclair release. + // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager + setRoutingP(MODE_INVALID, on ? ROUTE_SPEAKER: 0, ROUTE_SPEAKER); } /** @@ -651,7 +653,7 @@ public class AudioManager { * @return true if speakerphone is on, false if it's off */ public boolean isSpeakerphoneOn() { - return (getRouting(MODE_IN_CALL) & ROUTE_SPEAKER) == 0 ? false : true; + return (getRoutingP(MODE_IN_CALL) & ROUTE_SPEAKER) == 0 ? false : true; } /** @@ -661,14 +663,9 @@ public class AudioManager { * headset; <var>false</var> to route audio to/from phone earpiece */ public void setBluetoothScoOn(boolean on){ - // Don't disable A2DP when turning off SCO. - // A2DP does not affect in-call routing. - setRouting(MODE_RINGTONE, - on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_NORMAL, - on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_IN_CALL, - on ? ROUTE_BLUETOOTH_SCO: ROUTE_EARPIECE, ROUTE_ALL); + // Temporary fix for issue #1713090 until audio routing is refactored in eclair release. + // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager + setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_SCO: 0, ROUTE_BLUETOOTH_SCO); } /** @@ -678,7 +675,7 @@ public class AudioManager { * false if otherwise */ public boolean isBluetoothScoOn() { - return (getRouting(MODE_IN_CALL) & ROUTE_BLUETOOTH_SCO) == 0 ? false : true; + return (getRoutingP(MODE_IN_CALL) & ROUTE_BLUETOOTH_SCO) == 0 ? false : true; } /** @@ -688,12 +685,9 @@ public class AudioManager { * headset; <var>false</var> disable A2DP audio */ public void setBluetoothA2dpOn(boolean on){ - // the audio flinger chooses A2DP as a higher priority, - // so there is no need to disable other routes. - setRouting(MODE_RINGTONE, - on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_NORMAL, - on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); + // Temporary fix for issue #1713090 until audio routing is refactored in eclair release. + // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager + setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP); } /** @@ -703,7 +697,7 @@ public class AudioManager { * false if otherwise */ public boolean isBluetoothA2dpOn() { - return (getRouting(MODE_NORMAL) & ROUTE_BLUETOOTH_A2DP) == 0 ? false : true; + return (getRoutingP(MODE_NORMAL) & ROUTE_BLUETOOTH_A2DP) == 0 ? false : true; } /** @@ -714,14 +708,9 @@ public class AudioManager { * @hide */ public void setWiredHeadsetOn(boolean on){ - // A2DP has higher priority than wired headset, so headset connect/disconnect events - // should not affect A2DP routing - setRouting(MODE_NORMAL, - on ? ROUTE_HEADSET : ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_RINGTONE, - on ? ROUTE_HEADSET | ROUTE_SPEAKER : ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP); - setRouting(MODE_IN_CALL, - on ? ROUTE_HEADSET : ROUTE_EARPIECE, ROUTE_ALL); + // Temporary fix for issue #1713090 until audio routing is refactored in eclair release. + // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager + setRoutingP(MODE_INVALID, on ? ROUTE_HEADSET: 0, ROUTE_HEADSET); } /** @@ -732,7 +721,7 @@ public class AudioManager { * @hide */ public boolean isWiredHeadsetOn() { - return (getRouting(MODE_NORMAL) & ROUTE_HEADSET) == 0 ? false : true; + return (getRoutingP(MODE_NORMAL) & ROUTE_HEADSET) == 0 ? false : true; } /** @@ -860,7 +849,11 @@ public class AudioManager { * more of ROUTE_xxx types. Set bits indicate that route should be on * @param mask bit vector of routes to change, created from one or more of * ROUTE_xxx types. Unset bits indicate the route should be left unchanged + * + * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(), + * setBluetoothScoOn(), setBluetoothA2dpOn() and setWiredHeadsetOn() methods instead. */ + public void setRouting(int mode, int routes, int mask) { IAudioService service = getService(); try { @@ -876,7 +869,10 @@ public class AudioManager { * @param mode audio mode to get route (e.g., MODE_RINGTONE) * @return an audio route bit vector that can be compared with ROUTE_xxx * bits + * @deprecated Do not query audio routing directly, use isSpeakerphoneOn(), + * isBluetoothScoOn(), isBluetoothA2dpOn() and isWiredHeadsetOn() methods instead. */ + @Deprecated public int getRouting(int mode) { IAudioService service = getService(); try { @@ -1076,4 +1072,31 @@ public class AudioManager { * {@hide} */ private IBinder mICallBack = new Binder(); + + /** + * {@hide} + */ + private void setRoutingP(int mode, int routes, int mask) { + IAudioService service = getService(); + try { + service.setRouting(mode, routes, mask); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in setRouting", e); + } + } + + + /** + * {@hide} + */ + private int getRoutingP(int mode) { + IAudioService service = getService(); + try { + return service.getRouting(mode); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in getRouting", e); + return -1; + } + } + } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 2e3e46014136..881de4d2a468 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -100,6 +100,10 @@ public class AudioService extends IAudioService.Stub { private int[] mRoutes = new int[AudioSystem.NUM_MODES]; private Object mSettingsLock = new Object(); private boolean mMediaServerOk; + private boolean mSpeakerIsOn; + private boolean mBluetoothScoIsConnected; + private boolean mHeadsetIsConnected; + private boolean mBluetoothA2dpIsConnected; private SoundPool mSoundPool; private Object mSoundEffectsLock = new Object(); @@ -189,6 +193,10 @@ public class AudioService extends IAudioService.Stub { mMediaServerOk = true; AudioSystem.setErrorCallback(mAudioSystemCallback); loadSoundEffects(); + mSpeakerIsOn = false; + mBluetoothScoIsConnected = false; + mHeadsetIsConnected = false; + mBluetoothA2dpIsConnected = false; } private void createAudioSystemThread() { @@ -606,8 +614,9 @@ public class AudioService extends IAudioService.Stub { } synchronized (mSettingsLock) { if (mode != mMode) { - AudioSystem.setMode(mode); - mMode = mode; + if (AudioSystem.setMode(mode) == AudioSystem.AUDIO_STATUS_OK) { + mMode = mode; + } } int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int index = mStreamStates[streamType].mIndex; @@ -623,18 +632,167 @@ public class AudioService extends IAudioService.Stub { /** @see AudioManager#setRouting(int, int, int) */ public void setRouting(int mode, int routes, int mask) { + int incallMask = 0; + int ringtoneMask = 0; + int normalMask = 0; + if (!checkAudioSettingsPermission("setRouting()")) { return; } synchronized (mSettingsLock) { - if ((mRoutes[mode] & mask) != (routes & mask)) { - AudioSystem.setRouting(mode, routes, mask); - mRoutes[mode] = (mRoutes[mode] & ~mask) | (routes & mask); + // Temporary fix for issue #1713090 until audio routing is refactored in eclair release. + // mode AudioSystem.MODE_INVALID is used only by the following AudioManager methods: + // setWiredHeadsetOn(), setBluetoothA2dpOn(), setBluetoothScoOn() and setSpeakerphoneOn(). + // If applications are using AudioManager.setRouting() that is now deprecated, the routing + // command will be ignored. + if (mode == AudioSystem.MODE_INVALID) { + switch (mask) { + case AudioSystem.ROUTE_SPEAKER: + // handle setSpeakerphoneOn() + if (routes != 0 && !mSpeakerIsOn) { + mSpeakerIsOn = true; + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; + incallMask = AudioSystem.ROUTE_ALL; + } else if (mSpeakerIsOn) { + mSpeakerIsOn = false; + if (mBluetoothScoIsConnected) { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO; + } else if (mHeadsetIsConnected) { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; + } else { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE; + } + incallMask = AudioSystem.ROUTE_ALL; + } + break; + + case AudioSystem.ROUTE_BLUETOOTH_SCO: + // handle setBluetoothScoOn() + if (routes != 0 && !mBluetoothScoIsConnected) { + mBluetoothScoIsConnected = true; + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO; + mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_BLUETOOTH_SCO; + mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_BLUETOOTH_SCO; + incallMask = AudioSystem.ROUTE_ALL; + // A2DP has higher priority than SCO headset, so headset connect/disconnect events + // should not affect A2DP routing + ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + } else if (mBluetoothScoIsConnected) { + mBluetoothScoIsConnected = false; + if (mHeadsetIsConnected) { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; + mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER); + mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_HEADSET; + } else { + if (mSpeakerIsOn) { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; + } else { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE; + } + mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_SPEAKER; + mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_SPEAKER; + } + incallMask = AudioSystem.ROUTE_ALL; + // A2DP has higher priority than SCO headset, so headset connect/disconnect events + // should not affect A2DP routing + ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + } + break; + + case AudioSystem.ROUTE_HEADSET: + // handle setWiredHeadsetOn() + if (routes != 0 && !mHeadsetIsConnected) { + mHeadsetIsConnected = true; + // do not act upon headset connection if bluetooth SCO is connected to match phone app behavior + if (!mBluetoothScoIsConnected) { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; + mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER); + mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_HEADSET; + incallMask = AudioSystem.ROUTE_ALL; + // A2DP has higher priority than wired headset, so headset connect/disconnect events + // should not affect A2DP routing + ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + } + } else if (mHeadsetIsConnected) { + mHeadsetIsConnected = false; + // do not act upon headset disconnection if bluetooth SCO is connected to match phone app behavior + if (!mBluetoothScoIsConnected) { + if (mSpeakerIsOn) { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; + } else { + mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE; + } + mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_SPEAKER; + mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | + AudioSystem.ROUTE_SPEAKER; + + incallMask = AudioSystem.ROUTE_ALL; + // A2DP has higher priority than wired headset, so headset connect/disconnect events + // should not affect A2DP routing + ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + } + } + break; + + case AudioSystem.ROUTE_BLUETOOTH_A2DP: + // handle setBluetoothA2dpOn() + if (routes != 0 && !mBluetoothA2dpIsConnected) { + mBluetoothA2dpIsConnected = true; + mRoutes[AudioSystem.MODE_RINGTONE] |= AudioSystem.ROUTE_BLUETOOTH_A2DP; + mRoutes[AudioSystem.MODE_NORMAL] |= AudioSystem.ROUTE_BLUETOOTH_A2DP; + // the audio flinger chooses A2DP as a higher priority, + // so there is no need to disable other routes. + ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; + normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; + } else if (mBluetoothA2dpIsConnected) { + mBluetoothA2dpIsConnected = false; + mRoutes[AudioSystem.MODE_RINGTONE] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + mRoutes[AudioSystem.MODE_NORMAL] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP; + // the audio flinger chooses A2DP as a higher priority, + // so there is no need to disable other routes. + ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; + normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; + } + break; + } + + // incallMask is != 0 means we must apply ne routing to MODE_IN_CALL mode + if (incallMask != 0) { + AudioSystem.setRouting(AudioSystem.MODE_IN_CALL, + mRoutes[AudioSystem.MODE_IN_CALL], + incallMask); + } + // ringtoneMask is != 0 means we must apply ne routing to MODE_RINGTONE mode + if (ringtoneMask != 0) { + AudioSystem.setRouting(AudioSystem.MODE_RINGTONE, + mRoutes[AudioSystem.MODE_RINGTONE], + ringtoneMask); + } + // normalMask is != 0 means we must apply ne routing to MODE_NORMAL mode + if (normalMask != 0) { + AudioSystem.setRouting(AudioSystem.MODE_NORMAL, + mRoutes[AudioSystem.MODE_NORMAL], + normalMask); + } + + int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); + int index = mStreamStates[streamType].mIndex; + syncRingerAndNotificationStreamVolume(streamType, index, true); + setStreamVolumeInt(streamType, index, true); } - int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); - int index = mStreamStates[streamType].mIndex; - syncRingerAndNotificationStreamVolume(streamType, index, true); - setStreamVolumeInt(streamType, index, true); } } diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java index 0901fbfced07..4b53756db118 100644 --- a/media/java/android/media/ToneGenerator.java +++ b/media/java/android/media/ToneGenerator.java @@ -130,25 +130,35 @@ public class ToneGenerator */ public static final int TONE_DTMF_D = 15; /** - * Call supervisory tone, Dial tone: 425Hz, continuous - * + * Call supervisory tone, Dial tone: + * CEPT: 425Hz, continuous + * ANSI (IS-95): 350Hz+440Hz, continuous + * JAPAN: 400Hz, continuous + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_DIAL = 16; /** - * Call supervisory tone, Busy: 425Hz, 500ms ON, 500ms OFF... - * + * Call supervisory tone, Busy: + * CEPT: 425Hz, 500ms ON, 500ms OFF... + * ANSI (IS-95): 480Hz+620Hz, 500ms ON, 500ms OFF... + * JAPAN: 400Hz, 500ms ON, 500ms OFF... + * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_BUSY = 17; /** - * Call supervisory tone, Congestion: 425Hz, 200ms ON, 200ms OFF... + * Call supervisory tone, Congestion: + * CEPT, JAPAN: 425Hz, 200ms ON, 200ms OFF... + * ANSI (IS-95): 480Hz+620Hz, 250ms ON, 250ms OFF... * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_CONGESTION = 18; /** - * Call supervisory tone, Radio path acknowlegment : 425Hz, 200ms ON + * Call supervisory tone, Radio path acknowlegment : + * CEPT, ANSI: 425Hz, 200ms ON + * JAPAN: 400Hz, 1s ON, 2s OFF... * * @see #ToneGenerator(int, int) */ @@ -166,13 +176,17 @@ public class ToneGenerator */ public static final int TONE_SUP_ERROR = 21; /** - * Call supervisory tone, Call Waiting: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... + * Call supervisory tone, Call Waiting: + * CEPT, JAPAN: 425Hz, 200ms ON, 600ms OFF, 200ms ON, 3s OFF... + * ANSI (IS-95): 440 Hz, 300 ms ON, 9.7 s OFF, (100 ms ON, 100 ms OFF, 100 ms ON, 9.7s OFF ...) * * @see #ToneGenerator(int, int) */ public static final int TONE_SUP_CALL_WAITING = 22; /** - * Call supervisory tone, Ring Tone: 425Hz, 1s ON, 4s OFF... + * Call supervisory tone, Ring Tone: + * CEPT, JAPAN: 425Hz, 1s ON, 4s OFF... + * ANSI (IS-95): 440Hz + 480Hz, 2s ON, 4s OFF... * * @see #ToneGenerator(int, int) */ @@ -207,6 +221,37 @@ public class ToneGenerator * @see #ToneGenerator(int, int) */ public static final int TONE_PROP_BEEP2 = 28; + /** + * Call supervisory tone (IS-95), intercept tone: alternating 440 Hz and 620 Hz tones, each on for 250 ms + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_SUP_INTERCEPT = 29; + /** + * Call supervisory tone (IS-95), abbreviated intercept: intercept tone limited to 4 seconds + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_SUP_INTERCEPT_ABBREV = 30; + /** + * Call supervisory tone (IS-95), abbreviated congestion: congestion tone limited to 4 seconds + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_SUP_CONGESTION_ABBREV = 31; + /** + * Call supervisory tone (IS-95), confirm tone: a 350 Hz tone added to a 440 Hz tone repeated 3 times in a 100 ms on, 100 ms off cycle + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_SUP_CONFIRM = 32; + /** + * Call supervisory tone (IS-95), pip tone: four bursts of 480 Hz tone (0.1 s on, 0.1 s off). + * + * @see #ToneGenerator(int, int) + */ + public static final int TONE_SUP_PIP = 33; + /** Maximum volume, for use with {@link #ToneGenerator(int,int)} */ public static final int MAX_VOLUME = AudioSystem.MAX_VOLUME; @@ -258,6 +303,11 @@ public class ToneGenerator * <li>{@link #TONE_PROP_NACK} * <li>{@link #TONE_PROP_PROMPT} * <li>{@link #TONE_PROP_BEEP2} + * <li>{@link #TONE_SUP_INTERCEPT} + * <li>{@link #TONE_SUP_INTERCEPT_ABBREV} + * <li>{@link #TONE_SUP_CONGESTION_ABBREV} + * <li>{@link #TONE_SUP_CONFIRM} + * <li>{@link #TONE_SUP_PIP} * </ul> * @see #ToneGenerator(int, int) */ diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp index 856059328e4b..d1789addb922 100644 --- a/media/libmedia/ToneGenerator.cpp +++ b/media/libmedia/ToneGenerator.cpp @@ -24,44 +24,235 @@ #include <sys/resource.h> #include <utils/RefBase.h> #include <utils/Timers.h> +#include <cutils/properties.h> #include "media/ToneGenerator.h" + namespace android { + // Descriptors for all available tones (See ToneGenerator::ToneDescriptor class declaration for details) -const ToneGenerator::ToneDescriptor - ToneGenerator::toneDescriptors[NUM_TONES] = { - // waveFreq[] segments[] repeatCnt - { { 1336, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_0 - { { 1209, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_1 - { { 1336, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_2 - { { 1477, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_3 - { { 1209, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_4 - { { 1336, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_5 - { { 1477, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_6 - { { 1209, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_7 - { { 1336, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_8 - { { 1477, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_9 - { { 1209, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_S - { { 1477, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_P - { { 1633, 697, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_A - { { 1633, 770, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_B - { { 1633, 852, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_C - { { 1633, 941, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_DTMF_D - { { 425, 0 }, { ToneGenerator::TONEGEN_INF, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_DIAL - { { 425, 0 }, { 500, 500, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_BUSY - { { 425, 0 }, { 200, 200, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_CONGESTION - { { 425, 0 }, { 200, 0 }, 0 }, // TONE_SUP_RADIO_ACK - { { 425, 0 }, { 200, 200, 0 }, 2 }, // TONE_SUP_RADIO_NOTAVAIL - { { 950, 1400, 1800, 0 }, { 330, 1000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_ERROR - { { 425, 0 }, { 200, 600, 200, 3000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_CALL_WAITING - { { 425, 0 }, { 1000, 4000, 0 }, ToneGenerator::TONEGEN_INF }, // TONE_SUP_RINGTONE - { { 400, 1200, 0 }, { 40, 0 }, 0 }, // TONE_PROP_BEEP - { { 1200, 0 }, { 100, 100, 0 }, 1 }, // TONE_PROP_ACK - { { 300, 400, 500, 0 }, { 400, 0 }, 0 }, // TONE_PROP_NACK - { { 400, 1200, 0 }, { 200, 0 }, 0 }, // TONE_PROP_PROMPT - { { 400, 1200, 0 }, { 40, 200, 40, 0 }, 0 } // TONE_PROP_BEEP2 - }; +const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = { + { segments: {{ duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 941, 0 }}, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_0 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 697, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_1 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 697, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_2 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 697, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_3 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 770, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_4 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 770, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_5 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 770, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_6 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 852, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_7 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1336, 852, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_8 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 852, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_9 + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1209, 941, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_S + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1477, 941, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_P + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 697, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_A + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 770, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_B + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 852, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_C + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 1633, 941, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_DTMF_D + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 425, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_DIAL + { segments: { { duration: 500 , waveFreq: { 425, 0 }}, + { duration: 500, waveFreq: { 0 }}, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_BUSY + { segments: { { duration: 200, waveFreq: { 425, 0 } }, + { duration: 200, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_CONGESTION + { segments: { { duration: 200, waveFreq: { 425, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_SUP_RADIO_ACK + { segments: { { duration: 200, waveFreq: { 425, 0 }}, + { duration: 200, waveFreq: { 0 }}, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 2, + repeatSegment: 0 }, // TONE_SUP_RADIO_NOTAVAIL + { segments: { { duration: 330, waveFreq: { 950, 1400, 1800, 0 }}, + { duration: 1000, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_ERROR + { segments: { { duration: 200, waveFreq: { 425, 0 } }, + { duration: 600, waveFreq: { 0 } }, + { duration: 200, waveFreq: { 425, 0 } }, + { duration: 3000, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_CALL_WAITING + { segments: { { duration: 1000, waveFreq: { 425, 0 } }, + { duration: 4000, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_RINGTONE + { segments: { { duration: 40, waveFreq: { 400, 1200, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_PROP_BEEP + { segments: { { duration: 100, waveFreq: { 1200, 0 } }, + { duration: 100, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 1, + repeatSegment: 0 }, // TONE_PROP_ACK + { segments: { { duration: 400, waveFreq: { 300, 400, 500, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_PROP_NACK + { segments: { { duration: 200, waveFreq: { 400, 1200, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_PROP_PROMPT + { segments: { { duration: 40, waveFreq: { 400, 1200, 0 } }, + { duration: 200, waveFreq: { 0 } }, + { duration: 40, waveFreq: { 400, 1200, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 0, + repeatSegment: 0 }, // TONE_PROP_BEEP2 + { segments: { { duration: 250, waveFreq: { 440, 0 } }, + { duration: 250, waveFreq: { 620, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_SUP_INTERCEPT + { segments: { { duration: 250, waveFreq: { 440, 0 } }, + { duration: 250, waveFreq: { 620, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 7, + repeatSegment: 0 }, // TONE_SUP_INTERCEPT_ABBREV + { segments: { { duration: 250, waveFreq: { 480, 620, 0 } }, + { duration: 250, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 7, + repeatSegment: 0 }, // TONE_SUP_CONGESTION_ABBREV + { segments: { { duration: 100, waveFreq: { 350, 440, 0 } }, + { duration: 100, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 2, + repeatSegment: 0 }, // TONE_SUP_CONFIRM + { segments: { { duration: 100, waveFreq: { 480, 0 } }, + { duration: 100, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: 3, + repeatSegment: 0 }, // TONE_SUP_PIP + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 350, 440, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_ANSI_DIAL + { segments: { { duration: 500, waveFreq: { 480, 620, 0 } }, + { duration: 500, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_ANSI_BUSY + { segments: { { duration: 250, waveFreq: { 480, 620, 0 } }, + { duration: 250, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_ANSI_CONGESTION + { segments: { { duration: 300, waveFreq: { 440, 0 } }, + { duration: 9700, waveFreq: { 0 } }, + { duration: 100, waveFreq: { 440, 0 } }, + { duration: 100, waveFreq: { 0 } }, + { duration: 100, waveFreq: { 440, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 1 }, // TONE_ANSI_CALL_WAITING + { segments: { { duration: 2000, waveFreq: { 440, 480, 0 } }, + { duration: 4000, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_ANSI_RINGTONE + { segments: { { duration: ToneGenerator::TONEGEN_INF, waveFreq: { 400, 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_JAPAN_DIAL + { segments: { { duration: 500, waveFreq: { 400, 0 } }, + { duration: 500, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_JAPAN_BUSY + { segments: { { duration: 1000, waveFreq: { 400, 0 } }, + { duration: 2000, waveFreq: { 0 } }, + { duration: 0 , waveFreq: { 0 }}}, + repeatCnt: ToneGenerator::TONEGEN_INF, + repeatSegment: 0 }, // TONE_JAPAN_RADIO_ACK +}; + +// Used by ToneGenerator::getToneForRegion() to convert user specified supervisory tone type +// to actual tone for current region. +const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONES] = { + { // ANSI + TONE_ANSI_DIAL, // TONE_SUP_DIAL + TONE_ANSI_BUSY, // TONE_SUP_BUSY + TONE_ANSI_CONGESTION, // TONE_SUP_CONGESTION + TONE_SUP_RADIO_ACK, // TONE_SUP_RADIO_ACK + TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL + TONE_SUP_ERROR, // TONE_SUP_ERROR + TONE_ANSI_CALL_WAITING, // TONE_SUP_CALL_WAITING + TONE_ANSI_RINGTONE // TONE_SUP_RINGTONE + }, + { // JAPAN + TONE_JAPAN_DIAL, // TONE_SUP_DIAL + TONE_JAPAN_BUSY, // TONE_SUP_BUSY + TONE_SUP_CONGESTION, // TONE_SUP_CONGESTION + TONE_JAPAN_RADIO_ACK, // TONE_SUP_RADIO_ACK + TONE_SUP_RADIO_NOTAVAIL, // TONE_SUP_RADIO_NOTAVAIL + TONE_SUP_ERROR, // TONE_SUP_ERROR + TONE_SUP_CALL_WAITING, // TONE_SUP_CALL_WAITING + TONE_SUP_RINGTONE // TONE_SUP_RINGTONE + } +}; + //////////////////////////////////////////////////////////////////////////////// // ToneGenerator class Implementation @@ -105,6 +296,17 @@ ToneGenerator::ToneGenerator(int streamType, float volume) { // Generate tone by chunks of 20 ms to keep cadencing precision mProcessSize = (mSamplingRate * 20) / 1000; + char value[PROPERTY_VALUE_MAX]; + property_get("gsm.operator.iso-country", value, ""); + if (strcmp(value,"us") == 0 || + strcmp(value,"ca") == 0) { + mRegion = ANSI; + } else if (strcmp(value,"jp") == 0) { + mRegion = JAPAN; + } else { + mRegion = CEPT; + } + if (initAudioTrack()) { LOGV("ToneGenerator INIT OK, time: %d\n", (unsigned int)(systemTime()/1000000)); } else { @@ -170,7 +372,8 @@ bool ToneGenerator::startTone(int toneType) { mLock.lock(); // Get descriptor for requested tone - mpNewToneDesc = &toneDescriptors[toneType]; + toneType = getToneForRegion(toneType); + mpNewToneDesc = &sToneDescriptors[toneType]; if (mState == TONE_INIT) { if (prepareWave()) { @@ -333,6 +536,7 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { ToneGenerator *lpToneGen = static_cast<ToneGenerator *>(user); short *lpOut = buffer->i16; unsigned int lNumSmp = buffer->size/sizeof(short); + const ToneDescriptor *lpToneDesc = lpToneGen->mpToneDesc; if (buffer->size == 0) return; @@ -377,7 +581,7 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { // Exit if tone sequence is over - if (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] == 0) { + if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { if (lpToneGen->mState == TONE_PLAYING) { lpToneGen->mState = TONE_STOPPING; } @@ -390,52 +594,64 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { LOGV("End Segment, time: %d\n", (unsigned int)(systemTime()/1000000)); lGenSmp = lReqSmp; - - if (lpToneGen->mCurSegment & 0x0001) { - // If odd segment, OFF -> ON transition : reset wave generator - lWaveCmd = WaveGenerator::WAVEGEN_START; - - LOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); - } else { - // If even segment, ON -> OFF transition : ramp volume down + + // If segment, ON -> OFF transition : ramp volume down + if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] != 0) { lWaveCmd = WaveGenerator::WAVEGEN_STOP; - + unsigned int lFreqIdx = 0; + unsigned short lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx]; + + while (lFrequency != 0) { + WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency); + lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); + lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx]; + } LOGV("ON->OFF, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); } - - // Pre increment segment index and handle loop if last segment reached - if (lpToneGen->mpToneDesc->segments[++lpToneGen->mCurSegment] == 0) { + + // Go to next segment + lpToneGen->mCurSegment++; + + // Handle loop if last segment reached + if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) { LOGV("Last Seg: %d\n", lpToneGen->mCurSegment); // Pre increment loop count and restart if total count not reached. Stop sequence otherwise - if (++lpToneGen->mCurCount <= lpToneGen->mpToneDesc->repeatCnt) { + if (++lpToneGen->mCurCount <= lpToneDesc->repeatCnt) { LOGV("Repeating Count: %d\n", lpToneGen->mCurCount); - lpToneGen->mCurSegment = 0; + lpToneGen->mCurSegment = lpToneDesc->repeatSegment; + if (lpToneDesc->segments[lpToneDesc->repeatSegment].waveFreq[0] != 0) { + lWaveCmd = WaveGenerator::WAVEGEN_START; + } LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); } else { + lGenSmp = 0; LOGV("End repeat, time: %d\n", (unsigned int)(systemTime()/1000000)); - - // Cancel OFF->ON transition in case previous segment tone state was OFF - if (!(lpToneGen->mCurSegment & 0x0001)) { - lGenSmp = 0; - } } } else { LOGV("New segment %d, Next Time: %d\n", lpToneGen->mCurSegment, (lpToneGen->mNextSegSmp*1000)/lpToneGen->mSamplingRate); + if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] != 0) { + // If next segment is not silent, OFF -> ON transition : reset wave generator + lWaveCmd = WaveGenerator::WAVEGEN_START; + + LOGV("OFF->ON, lGenSmp: %d, lReqSmp: %d\n", lGenSmp, lReqSmp); + } else { + lGenSmp = 0; + } } // Update next segment transition position. No harm to do it also for last segment as lpToneGen->mNextSegSmp won't be used any more lpToneGen->mNextSegSmp - += (lpToneGen->mpToneDesc->segments[lpToneGen->mCurSegment] * lpToneGen->mSamplingRate) / 1000; + += (lpToneDesc->segments[lpToneGen->mCurSegment].duration * lpToneGen->mSamplingRate) / 1000; } else { // Inside a segment keep tone ON or OFF - if (lpToneGen->mCurSegment & 0x0001) { + if (lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[0] == 0) { lGenSmp = 0; // If odd segment, tone is currently OFF } else { lGenSmp = lReqSmp; // If event segment, tone is currently ON @@ -444,11 +660,13 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) { if (lGenSmp) { // If samples must be generated, call all active wave generators and acumulate waves in lpOut - unsigned int lWaveIdx; + unsigned int lFreqIdx = 0; + unsigned short lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx]; - for (lWaveIdx = 0; lWaveIdx < (unsigned int)lpToneGen->mWaveGens.size(); lWaveIdx++) { - WaveGenerator *lpWaveGen = lpToneGen->mWaveGens[lWaveIdx]; + while (lFrequency != 0) { + WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency); lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd); + lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx]; } } @@ -501,7 +719,7 @@ audioCallback_EndLoop: // Method: ToneGenerator::prepareWave() // // Description: Prepare wave generators and reset tone sequencer state machine. -// mpNewToneDesc must have been initialized befoire calling this function. +// mpNewToneDesc must have been initialized before calling this function. // Input: // none // @@ -510,40 +728,48 @@ audioCallback_EndLoop: // //////////////////////////////////////////////////////////////////////////////// bool ToneGenerator::prepareWave() { - unsigned int lCnt = 0; - unsigned int lNumWaves; + unsigned int segmentIdx = 0; if (!mpNewToneDesc) { return false; } + // Remove existing wave generators if any clearWaveGens(); mpToneDesc = mpNewToneDesc; - // Get total number of sine waves: needed to adapt sine wave gain. - lNumWaves = numWaves(); - - // Instantiate as many wave generators as listed in descriptor - while (lCnt < lNumWaves) { - ToneGenerator::WaveGenerator *lpWaveGen = - new ToneGenerator::WaveGenerator((unsigned short)mSamplingRate, - mpToneDesc->waveFreq[lCnt], - TONEGEN_GAIN/lNumWaves); - if (lpWaveGen == 0) { - goto prepareWave_exit; + while (mpToneDesc->segments[segmentIdx].duration) { + // Get total number of sine waves: needed to adapt sine wave gain. + unsigned int lNumWaves = numWaves(segmentIdx); + unsigned int freqIdx = 0; + unsigned int frequency = mpToneDesc->segments[segmentIdx].waveFreq[freqIdx]; + while (frequency) { + // Instantiate a wave generator if ot already done for this frequency + if (mWaveGens.indexOfKey(frequency) == NAME_NOT_FOUND) { + ToneGenerator::WaveGenerator *lpWaveGen = + new ToneGenerator::WaveGenerator((unsigned short)mSamplingRate, + frequency, + TONEGEN_GAIN/lNumWaves); + if (lpWaveGen == 0) { + goto prepareWave_exit; + } + mWaveGens.add(frequency, lpWaveGen); + } + frequency = mpNewToneDesc->segments[segmentIdx].waveFreq[++freqIdx]; } - - mWaveGens.push(lpWaveGen); - LOGV("Create sine: %d\n", mpToneDesc->waveFreq[lCnt]); - lCnt++; + segmentIdx++; } // Initialize tone sequencer mTotalSmp = 0; mCurSegment = 0; mCurCount = 0; - mNextSegSmp = (mpToneDesc->segments[0] * mSamplingRate) / 1000; + if (mpToneDesc->segments[0].duration == TONEGEN_INF) { + mNextSegSmp = TONEGEN_INF; + } else{ + mNextSegSmp = (mpToneDesc->segments[0].duration * mSamplingRate) / 1000; + } return true; @@ -559,19 +785,22 @@ prepareWave_exit: // // Method: ToneGenerator::numWaves() // -// Description: Count number of sine waves needed to generate tone (e.g 2 for DTMF). +// Description: Count number of sine waves needed to generate a tone segment (e.g 2 for DTMF). // // Input: -// none +// segmentIdx tone segment index // // Output: // returned value: nummber of sine waves // //////////////////////////////////////////////////////////////////////////////// -unsigned int ToneGenerator::numWaves() { +unsigned int ToneGenerator::numWaves(unsigned int segmentIdx) { unsigned int lCnt = 0; - while (mpToneDesc->waveFreq[lCnt]) { + if (mpToneDesc->segments[segmentIdx].duration) { + while (mpToneDesc->segments[segmentIdx].waveFreq[lCnt]) { + lCnt++; + } lCnt++; } @@ -595,10 +824,38 @@ unsigned int ToneGenerator::numWaves() { void ToneGenerator::clearWaveGens() { LOGV("Clearing mWaveGens:"); - while (!mWaveGens.isEmpty()) { - delete mWaveGens.top(); - mWaveGens.pop(); + for (size_t lIdx = 0; lIdx < mWaveGens.size(); lIdx++) { + delete mWaveGens.valueAt(lIdx); } + mWaveGens.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Method: ToneGenerator::getToneForRegion() +// +// Description: Get correct ringtone type according to current region. +// The corrected ring tone type is the tone descriptor index in sToneDescriptors[]. +// +// Input: +// none +// +// Output: +// none +// +//////////////////////////////////////////////////////////////////////////////// +int ToneGenerator::getToneForRegion(int toneType) { + int regionTone; + + if (mRegion == CEPT || toneType < FIRST_SUP_TONE || toneType > LAST_SUP_TONE) { + regionTone = toneType; + } else { + regionTone = sToneMappingTable[mRegion][toneType - FIRST_SUP_TONE]; + } + + LOGV("getToneForRegion, tone %d, region %d, regionTone %d", toneType, mRegion, regionTone); + + return regionTone; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java index da18e74f5ca4..c75b96b8746f 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/TonesAutoTest.java @@ -74,7 +74,19 @@ import android.media.AudioManager; break; } } - + + for (type = ToneGenerator.TONE_SUP_INTERCEPT; + type <= ToneGenerator.TONE_SUP_PIP; type++) { + if (toneGen.startTone(type)) { + Thread.sleep(5000); + toneGen.stopTone(); + Thread.sleep(200); + } else { + result = false; + break; + } + } + toneGen.release(); return result; } |