Merge "Change the 0_fully signal icon to the new style" into gingerbread
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 12cf853..bc3e767 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -165,7 +165,7 @@
private void drawOval(Canvas canvas, float x, float y, float major, float minor,
float angle, Paint paint) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.rotate((float) (angle * 180 / Math.PI), x, y);
+ canvas.rotate((float) (-angle * 180 / Math.PI), x, y);
mReusableOvalRect.left = x - minor / 2;
mReusableOvalRect.right = x + minor / 2;
mReusableOvalRect.top = y - major / 2;
diff --git a/core/res/res/drawable-hdpi/overscroll_edge.png b/core/res/res/drawable-hdpi/overscroll_edge.png
index f8e40ec..e8c1aa3 100644
--- a/core/res/res/drawable-hdpi/overscroll_edge.png
+++ b/core/res/res/drawable-hdpi/overscroll_edge.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/overscroll_glow.png b/core/res/res/drawable-hdpi/overscroll_glow.png
index a8a62c4..3418384 100644
--- a/core/res/res/drawable-hdpi/overscroll_glow.png
+++ b/core/res/res/drawable-hdpi/overscroll_glow.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
index c299e12..ae90cc8 100755
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
+++ b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_fully_in_e.png
Binary files differ
diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
index bceceda..af3e0886 100755
--- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java
@@ -126,14 +126,27 @@
return false;
}
- SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
- Log.v(LOG_TAG, " ++++++ taking call from: "
- + sipAudioCall.getPeerProfile().getUriString());
- String localUri = sipAudioCall.getLocalProfile().getUriString();
- if (localUri.equals(mProfile.getUriString())) {
- boolean makeCallWait = foregroundCall.getState().isAlive();
- ringingCall.initIncomingCall(sipAudioCall, makeCallWait);
- return true;
+ try {
+ SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
+ Log.d(LOG_TAG, "+++ taking call from: "
+ + sipAudioCall.getPeerProfile().getUriString());
+ String localUri = sipAudioCall.getLocalProfile().getUriString();
+ if (localUri.equals(mProfile.getUriString())) {
+ boolean makeCallWait = foregroundCall.getState().isAlive();
+ ringingCall.initIncomingCall(sipAudioCall, makeCallWait);
+ if (sipAudioCall.getState()
+ != SipSession.State.INCOMING_CALL) {
+ // Peer cancelled the call!
+ Log.d(LOG_TAG, " call cancelled !!");
+ ringingCall.reset();
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ // Peer may cancel the call at any time during the time we hook
+ // up ringingCall with sipAudioCall. Clean up ringingCall when
+ // that happens.
+ ringingCall.reset();
}
return false;
}
@@ -358,6 +371,11 @@
}
private class SipCall extends SipCallBase {
+ void reset() {
+ connections.clear();
+ setState(Call.State.IDLE);
+ }
+
void switchWith(SipCall that) {
synchronized (SipPhone.class) {
SipCall tmp = new SipCall();
@@ -444,6 +462,7 @@
if (state.isAlive()) {
Log.d(LOG_TAG, "hang up call: " + getState() + ": " + this
+ " on phone " + getPhone());
+ setState(State.DISCONNECTING);
CallStateException excp = null;
for (Connection c : connections) {
try {
@@ -453,7 +472,6 @@
}
}
if (excp != null) throw excp;
- setState(State.DISCONNECTING);
} else {
Log.d(LOG_TAG, "hang up dead call: " + getState() + ": "
+ this + " on phone " + getPhone());
@@ -630,13 +648,20 @@
}
synchronized (SipPhone.class) {
setState(Call.State.DISCONNECTED);
- mSipAudioCall.close();
- mOwner.onConnectionEnded(SipConnection.this);
- Log.v(LOG_TAG, "-------- connection ended: "
- + mPeer.getUriString() + ": "
- + mSipAudioCall.getState() + ", cause: "
- + getDisconnectCause() + ", on phone "
+ SipAudioCall sipAudioCall = mSipAudioCall;
+ mSipAudioCall = null;
+ String sessionState = (sipAudioCall == null)
+ ? ""
+ : (sipAudioCall.getState() + ", ");
+ Log.v(LOG_TAG, "--- connection ended: "
+ + mPeer.getUriString() + ": " + sessionState
+ + "cause: " + getDisconnectCause() + ", on phone "
+ getPhone());
+ if (sipAudioCall != null) {
+ sipAudioCall.setListener(null);
+ sipAudioCall.close();
+ }
+ mOwner.onConnectionEnded(SipConnection.this);
}
}
@@ -790,14 +815,17 @@
synchronized (SipPhone.class) {
Log.v(LOG_TAG, "hangup conn: " + mPeer.getUriString() + ": "
+ mState + ": on phone " + getPhone().getPhoneName());
+ if (!mState.isAlive()) return;
try {
- if (mState.isAlive()) {
- if (mSipAudioCall != null) mSipAudioCall.endCall();
- setState(Call.State.DISCONNECTING);
- setDisconnectCause(DisconnectCause.LOCAL);
+ SipAudioCall sipAudioCall = mSipAudioCall;
+ if (sipAudioCall != null) {
+ sipAudioCall.setListener(null);
+ sipAudioCall.endCall();
}
} catch (SipException e) {
throw new CallStateException("hangup(): " + e);
+ } finally {
+ mAdapter.onCallEnded(DisconnectCause.LOCAL);
}
}
}
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index f171806..3877aeb 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -80,8 +80,7 @@
*/
public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
- // TODO: add rest of the codecs when the native part is done.
- private static final AudioCodec[] sCodecs = {GSM_EFR, GSM, PCMU, PCMA};
+ private static final AudioCodec[] sCodecs = {GSM_EFR, AMR, GSM, PCMU, PCMA};
private AudioCodec(int type, String rtpmap, String fmtp) {
this.type = type;
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 59631c1..52f5716 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -520,7 +520,9 @@
private String getUri(ISipSession session) {
try {
- return session.getLocalProfile().getUriString();
+ return ((session == null)
+ ? "no session"
+ : session.getLocalProfile().getUriString());
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 0ff5586..130fe9f 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -510,31 +510,43 @@
}
}
+ // KeepAliveProcess is controlled by AutoRegistrationProcess.
+ // All methods will be invoked in sync with SipService.this except realRun()
private class KeepAliveProcess implements Runnable {
private static final String TAG = "\\KEEPALIVE/";
private static final int INTERVAL = 10;
private SipSessionGroup.SipSessionImpl mSession;
+ private boolean mRunning = false;
public KeepAliveProcess(SipSessionGroup.SipSessionImpl session) {
mSession = session;
}
public void start() {
+ if (mRunning) return;
+ mRunning = true;
mTimer.set(INTERVAL * 1000, this);
}
+ // timeout handler
public void run() {
+ if (!mRunning) return;
+ final SipSessionGroup.SipSessionImpl session = mSession;
+
// delegate to mExecutor
getExecutor().addTask(new Runnable() {
public void run() {
- realRun();
+ realRun(session);
}
});
}
- private void realRun() {
+ // real timeout handler
+ private void realRun(SipSessionGroup.SipSessionImpl session) {
synchronized (SipService.this) {
- SipSessionGroup.SipSessionImpl session = mSession.duplicate();
+ if (notCurrentSession(session)) return;
+
+ session = session.duplicate();
if (DEBUG) Log.d(TAG, "~~~ keepalive");
mTimer.cancel(this);
session.sendKeepAlive();
@@ -547,8 +559,14 @@
}
public void stop() {
+ mRunning = false;
+ mSession = null;
mTimer.cancel(this);
}
+
+ private boolean notCurrentSession(ISipSession session) {
+ return (session != mSession) || !mRunning;
+ }
}
private class AutoRegistrationProcess extends SipSessionAdapter
@@ -561,13 +579,15 @@
private long mExpiryTime;
private int mErrorCode;
private String mErrorMessage;
+ private boolean mRunning = false;
private String getAction() {
return toString();
}
public void start(SipSessionGroup group) {
- if (mSession == null) {
+ if (!mRunning) {
+ mRunning = true;
mBackoff = 1;
mSession = (SipSessionGroup.SipSessionImpl)
group.createSession(this);
@@ -584,35 +604,24 @@
}
public void stop() {
- stop(false);
- }
-
- private void stopButKeepStates() {
- stop(true);
- }
-
- private void stop(boolean keepStates) {
- if (mSession == null) return;
+ if (!mRunning) return;
+ mRunning = false;
+ mSession.setListener(null);
if (mConnected && mRegistered) mSession.unregister();
+
mTimer.cancel(this);
if (mKeepAliveProcess != null) {
mKeepAliveProcess.stop();
mKeepAliveProcess = null;
}
- if (!keepStates) {
- mSession = null;
- mRegistered = false;
- }
- }
- private boolean isStopped() {
- return (mSession == null);
+ mRegistered = false;
+ setListener(mProxy.getListener());
}
public void setListener(ISipSessionListener listener) {
synchronized (SipService.this) {
mProxy.setListener(listener);
- if (mSession == null) return;
try {
int state = (mSession == null)
@@ -632,6 +641,18 @@
mProxy.onRegistrationFailed(mSession, mErrorCode,
mErrorMessage);
}
+ } else if (!mConnected) {
+ mProxy.onRegistrationFailed(mSession,
+ SipErrorCode.DATA_CONNECTION_LOST,
+ "no data connection");
+ } else if (!mRunning) {
+ mProxy.onRegistrationFailed(mSession,
+ SipErrorCode.CLIENT_ERROR,
+ "registration not running");
+ } else {
+ mProxy.onRegistrationFailed(mSession,
+ SipErrorCode.IN_PROGRESS,
+ String.valueOf(state));
}
} catch (Throwable t) {
Log.w(TAG, "setListener(): " + t);
@@ -643,21 +664,29 @@
return mRegistered;
}
+ // timeout handler
public void run() {
- // delegate to mExecutor
- getExecutor().addTask(new Runnable() {
- public void run() {
- realRun();
- }
- });
+ synchronized (SipService.this) {
+ if (!mRunning) return;
+ final SipSessionGroup.SipSessionImpl session = mSession;
+
+ // delegate to mExecutor
+ getExecutor().addTask(new Runnable() {
+ public void run() {
+ realRun(session);
+ }
+ });
+ }
}
- private void realRun() {
- mErrorCode = SipErrorCode.NO_ERROR;
- mErrorMessage = null;
- if (DEBUG) Log.d(TAG, "~~~ registering");
+ // real timeout handler
+ private void realRun(SipSessionGroup.SipSessionImpl session) {
synchronized (SipService.this) {
- if (mConnected && !isStopped()) mSession.register(EXPIRY_TIME);
+ if (notCurrentSession(session)) return;
+ mErrorCode = SipErrorCode.NO_ERROR;
+ mErrorMessage = null;
+ if (DEBUG) Log.d(TAG, "~~~ registering");
+ if (mConnected) session.register(EXPIRY_TIME);
}
}
@@ -697,22 +726,29 @@
public void onRegistering(ISipSession session) {
if (DEBUG) Log.d(TAG, "onRegistering(): " + session);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
+ if (notCurrentSession(session)) return;
+
mRegistered = false;
mProxy.onRegistering(session);
}
}
+ private boolean notCurrentSession(ISipSession session) {
+ if (session != mSession) {
+ ((SipSessionGroup.SipSessionImpl) session).setListener(null);
+ return true;
+ }
+ return !mRunning;
+ }
+
@Override
public void onRegistrationDone(ISipSession session, int duration) {
if (DEBUG) Log.d(TAG, "onRegistrationDone(): " + session);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
+ if (notCurrentSession(session)) return;
mProxy.onRegistrationDone(session, duration);
- if (isStopped()) return;
-
if (duration > 0) {
mSession.clearReRegisterRequired();
mExpiryTime = SystemClock.elapsedRealtime()
@@ -751,17 +787,18 @@
if (DEBUG) Log.d(TAG, "onRegistrationFailed(): " + session + ": "
+ SipErrorCode.toString(errorCode) + ": " + message);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
- mErrorCode = errorCode;
- mErrorMessage = message;
- mProxy.onRegistrationFailed(session, errorCode, message);
+ if (notCurrentSession(session)) return;
if (errorCode == SipErrorCode.INVALID_CREDENTIALS) {
if (DEBUG) Log.d(TAG, " pause auto-registration");
- stopButKeepStates();
- } else if (!isStopped()) {
+ stop();
+ } else {
onError();
}
+
+ mErrorCode = errorCode;
+ mErrorMessage = message;
+ mProxy.onRegistrationFailed(session, errorCode, message);
}
}
@@ -769,14 +806,11 @@
public void onRegistrationTimeout(ISipSession session) {
if (DEBUG) Log.d(TAG, "onRegistrationTimeout(): " + session);
synchronized (SipService.this) {
- if (!isStopped() && (session != mSession)) return;
+ if (notCurrentSession(session)) return;
+
mErrorCode = SipErrorCode.TIME_OUT;
mProxy.onRegistrationTimeout(session);
-
- if (!isStopped()) {
- mRegistered = false;
- onError();
- }
+ onError();
}
}
@@ -883,6 +917,7 @@
mConnected = connected;
}
+ // timeout handler
@Override
public void run() {
// delegate to mExecutor
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 4321d7b..c68fa1b 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -334,12 +334,12 @@
if (isRequestEvent(Request.INVITE, evt)) {
RequestEvent event = (RequestEvent) evt;
SipSessionImpl newSession = new SipSessionImpl(mProxy);
+ newSession.mState = SipSession.State.INCOMING_CALL;
newSession.mServerTransaction = mSipHelper.sendRinging(event,
generateTag());
newSession.mDialog = newSession.mServerTransaction.getDialog();
newSession.mInviteReceived = event;
newSession.mPeerProfile = createPeerProfile(event.getRequest());
- newSession.mState = SipSession.State.INCOMING_CALL;
newSession.mPeerSessionDescription =
extractContent(event.getRequest());
addSipSession(newSession);
@@ -708,7 +708,6 @@
case SipSession.State.PINGING:
reset();
mReRegisterFlag = true;
- mState = SipSession.State.READY_TO_CALL;
break;
default:
@@ -877,6 +876,7 @@
private boolean readyForCall(EventObject evt) throws SipException {
// expect MakeCallCommand, RegisterCommand, DEREGISTER
if (evt instanceof MakeCallCommand) {
+ mState = SipSession.State.OUTGOING_CALL;
MakeCallCommand cmd = (MakeCallCommand) evt;
mPeerProfile = cmd.getPeerProfile();
mClientTransaction = mSipHelper.sendInvite(mLocalProfile,
@@ -884,25 +884,24 @@
generateTag());
mDialog = mClientTransaction.getDialog();
addSipSession(this);
- mState = SipSession.State.OUTGOING_CALL;
mProxy.onCalling(this);
startSessionTimer(cmd.getTimeout());
return true;
} else if (evt instanceof RegisterCommand) {
+ mState = SipSession.State.REGISTERING;
int duration = ((RegisterCommand) evt).getDuration();
mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
generateTag(), duration);
mDialog = mClientTransaction.getDialog();
addSipSession(this);
- mState = SipSession.State.REGISTERING;
mProxy.onRegistering(this);
return true;
} else if (DEREGISTER == evt) {
+ mState = SipSession.State.DEREGISTERING;
mClientTransaction = mSipHelper.sendRegister(mLocalProfile,
generateTag(), 0);
mDialog = mClientTransaction.getDialog();
addSipSession(this);
- mState = SipSession.State.DEREGISTERING;
mProxy.onRegistering(this);
return true;
}
@@ -913,11 +912,11 @@
// expect MakeCallCommand(answering) , END_CALL cmd , Cancel
if (evt instanceof MakeCallCommand) {
// answer call
+ mState = SipSession.State.INCOMING_CALL_ANSWERING;
mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
mLocalProfile,
((MakeCallCommand) evt).getSessionDescription(),
mServerTransaction);
- mState = SipSession.State.INCOMING_CALL_ANSWERING;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
} else if (END_CALL == evt) {
@@ -1009,8 +1008,8 @@
// RFC says that UA should not send out cancel when no
// response comes back yet. We are cheating for not checking
// response.
- mSipHelper.sendCancel(mClientTransaction);
mState = SipSession.State.OUTGOING_CALL_CANCELING;
+ mSipHelper.sendCancel(mClientTransaction);
startSessionTimer(CANCEL_CALL_TIMER);
return true;
}
@@ -1065,8 +1064,8 @@
return true;
} else if (isRequestEvent(Request.INVITE, evt)) {
// got Re-INVITE
- RequestEvent event = mInviteReceived = (RequestEvent) evt;
mState = SipSession.State.INCOMING_CALL;
+ RequestEvent event = mInviteReceived = (RequestEvent) evt;
mPeerSessionDescription = extractContent(event.getRequest());
mServerTransaction = null;
mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription);
@@ -1077,9 +1076,9 @@
return true;
} else if (evt instanceof MakeCallCommand) {
// to change call
+ mState = SipSession.State.OUTGOING_CALL;
mClientTransaction = mSipHelper.sendReinvite(mDialog,
((MakeCallCommand) evt).getSessionDescription());
- mState = SipSession.State.OUTGOING_CALL;
startSessionTimer(((MakeCallCommand) evt).getTimeout());
return true;
}
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
index 9a2227d..f3ecac2 100644
--- a/voip/jni/rtp/AmrCodec.cpp
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string.h>
+
#include "AudioCodec.h"
#include "gsmamr_dec.h"
@@ -21,6 +23,170 @@
namespace {
+const int gFrameBits[8] = {95, 103, 118, 134, 148, 159, 204, 244};
+
+//------------------------------------------------------------------------------
+
+// See RFC 4867 for the encoding details.
+
+class AmrCodec : public AudioCodec
+{
+public:
+ AmrCodec() {
+ if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
+ mEncoder = NULL;
+ }
+ if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
+ mDecoder = NULL;
+ }
+ }
+
+ ~AmrCodec() {
+ if (mEncoder) {
+ AMREncodeExit(&mEncoder, &mSidSync);
+ }
+ if (mDecoder) {
+ GSMDecodeFrameExit(&mDecoder);
+ }
+ }
+
+ int set(int sampleRate, const char *fmtp);
+ int encode(void *payload, int16_t *samples);
+ int decode(int16_t *samples, void *payload, int length);
+
+private:
+ void *mEncoder;
+ void *mSidSync;
+ void *mDecoder;
+
+ int mMode;
+ int mModeSet;
+ bool mOctetAligned;
+};
+
+int AmrCodec::set(int sampleRate, const char *fmtp)
+{
+ // These parameters are not supported.
+ if (strcasestr(fmtp, "crc=1") || strcasestr(fmtp, "robust-sorting=1") ||
+ strcasestr(fmtp, "interleaving=")) {
+ return -1;
+ }
+
+ // Handle mode-set and octet-align.
+ char *modes = strcasestr(fmtp, "mode-set=");
+ if (modes) {
+ mMode = 0;
+ mModeSet = 0;
+ for (char c = *modes; c && c != ' '; c = *++modes) {
+ if (c >= '0' && c <= '7') {
+ int mode = c - '0';
+ if (mode > mMode) {
+ mMode = mode;
+ }
+ mModeSet |= 1 << mode;
+ }
+ }
+ } else {
+ mMode = 7;
+ mModeSet = 0xFF;
+ }
+ mOctetAligned = (strcasestr(fmtp, "octet-align=1") != NULL);
+
+ // TODO: handle mode-change-*.
+
+ return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
+}
+
+int AmrCodec::encode(void *payload, int16_t *samples)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+
+ int length = AMREncode(mEncoder, mSidSync, (Mode)mMode,
+ samples, bytes + 1, &type, AMR_TX_WMF);
+
+ if (type != mMode || length != (8 + gFrameBits[mMode] + 7) >> 3) {
+ return -1;
+ }
+
+ if (mOctetAligned) {
+ bytes[0] = 0xF0;
+ bytes[1] = (mMode << 3) | 0x04;
+ ++length;
+ } else {
+ // CMR = 15 (4-bit), F = 0 (1-bit), FT = mMode (4-bit), Q = 1 (1-bit).
+ bytes[0] = 0xFF;
+ bytes[1] = 0xC0 | (mMode << 1) | 1;
+
+ // Shift left 6 bits and update the length.
+ bytes[length + 1] = 0;
+ for (int i = 0; i <= length; ++i) {
+ bytes[i] = (bytes[i] << 6) | (bytes[i + 1] >> 2);
+ }
+ length = (10 + gFrameBits[mMode] + 7) >> 3;
+ }
+ return length;
+}
+
+int AmrCodec::decode(int16_t *samples, void *payload, int length)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+ if (length < 2) {
+ return -1;
+ }
+ int request = bytes[0] >> 4;
+
+ if (mOctetAligned) {
+ if ((bytes[1] & 0xC4) != 0x04) {
+ return -1;
+ }
+ type = (Frame_Type_3GPP)(bytes[1] >> 3);
+ if (length != (16 + gFrameBits[type] + 7) >> 3) {
+ return -1;
+ }
+ length -= 2;
+ bytes += 2;
+ } else {
+ if ((bytes[0] & 0x0C) || !(bytes[1] & 0x40)) {
+ return -1;
+ }
+ type = (Frame_Type_3GPP)((bytes[0] << 1 | bytes[1] >> 7) & 0x07);
+ if (length != (10 + gFrameBits[type] + 7) >> 3) {
+ return -1;
+ }
+
+ // Shift left 2 bits and update the length.
+ --length;
+ for (int i = 1; i < length; ++i) {
+ bytes[i] = (bytes[i] << 2) | (bytes[i + 1] >> 6);
+ }
+ bytes[length] <<= 2;
+ length = (gFrameBits[type] + 7) >> 3;
+ ++bytes;
+ }
+
+ if (AMRDecode(mDecoder, type, bytes, samples, MIME_IETF) != length) {
+ return -1;
+ }
+
+ // Handle CMR
+ if (request < 8 && request != mMode) {
+ for (int i = request; i >= 0; --i) {
+ if (mModeSet & (1 << i)) {
+ mMode = request;
+ break;
+ }
+ }
+ }
+
+ return 160;
+}
+
+//------------------------------------------------------------------------------
+
+// See RFC 3551 for the encoding details.
+
class GsmEfrCodec : public AudioCodec
{
public:
@@ -91,6 +257,11 @@
} // namespace
+AudioCodec *newAmrCodec()
+{
+ return new AmrCodec;
+}
+
AudioCodec *newGsmEfrCodec()
{
return new GsmEfrCodec;
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index afc193c..2267ea0 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -21,6 +21,7 @@
extern AudioCodec *newAlawCodec();
extern AudioCodec *newUlawCodec();
extern AudioCodec *newGsmCodec();
+extern AudioCodec *newAmrCodec();
extern AudioCodec *newGsmEfrCodec();
struct AudioCodecType {
@@ -30,6 +31,7 @@
{"PCMA", newAlawCodec},
{"PCMU", newUlawCodec},
{"GSM", newGsmCodec},
+ {"AMR", newAmrCodec},
{"GSM-EFR", newGsmEfrCodec},
{NULL, NULL},
};