summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/widget/PopupWindow.java30
-rw-r--r--core/java/android/widget/TextView.java60
-rw-r--r--voip/java/android/net/sip/SipManager.java22
-rw-r--r--voip/java/com/android/server/sip/SipService.java80
-rw-r--r--voip/jni/rtp/AudioGroup.cpp58
5 files changed, 186 insertions, 64 deletions
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 7fe6190..ed15e25 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -86,6 +86,7 @@ public class PopupWindow {
private boolean mOutsideTouchable = false;
private boolean mClippingEnabled = true;
private boolean mSplitTouchEnabled;
+ private boolean mLayoutInScreen;
private OnTouchListener mTouchInterceptor;
@@ -596,6 +597,29 @@ public class PopupWindow {
}
/**
+ * <p>Indicates whether the popup window will be forced into using absolute screen coordinates
+ * for positioning.</p>
+ *
+ * @return true if the window will always be positioned in screen coordinates.
+ * @hide
+ */
+ public boolean isLayoutInScreenEnabled() {
+ return mLayoutInScreen;
+ }
+
+ /**
+ * <p>Allows the popup window to force the flag
+ * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN}, overriding default behavior.
+ * This will cause the popup to be positioned in absolute screen coordinates.</p>
+ *
+ * @param enabled true if the popup should always be positioned in screen coordinates
+ * @hide
+ */
+ public void setLayoutInScreenEnabled(boolean enabled) {
+ mLayoutInScreen = enabled;
+ }
+
+ /**
* <p>Change the width and height measure specs that are given to the
* window manager by the popup. By default these are 0, meaning that
* the current width or height is requested as an explicit size from
@@ -899,7 +923,8 @@ public class PopupWindow {
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
- WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH);
if(mIgnoreCheekPress) {
curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
}
@@ -923,6 +948,9 @@ public class PopupWindow {
if (mSplitTouchEnabled) {
curFlags |= WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
}
+ if (mLayoutInScreen) {
+ curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+ }
return curFlags;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6278192..8291d57 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3768,6 +3768,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mError != null) {
hideError();
}
+
+ hideControllers();
}
@Override
@@ -4118,6 +4120,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
canvas.restore();
+
+ if (mInsertionPointCursorController != null &&
+ mInsertionPointCursorController.isShowing()) {
+ mInsertionPointCursorController.updatePosition();
+ }
+ if (mSelectionModifierCursorController != null &&
+ mSelectionModifierCursorController.isShowing()) {
+ mSelectionModifierCursorController.updatePosition();
+ }
}
@Override
@@ -4736,6 +4747,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mInputMethodState != null) {
mInputMethodState.mExtracting = req;
}
+ hideControllers();
}
/**
@@ -6266,7 +6278,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
- hideControllers();
+
+ // Hide the controller if the amount of content changed
+ if (before != after) {
+ hideControllers();
+ }
}
/**
@@ -6605,11 +6621,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (mInputContentType != null) {
mInputContentType.enterDown = false;
}
+ hideControllers();
}
startStopMarquee(hasWindowFocus);
}
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (visibility != VISIBLE) {
+ hideControllers();
+ }
+ }
+
/**
* Use {@link BaseInputConnection#removeComposingSpans
* BaseInputConnection.removeComposingSpans()} to remove any IME composing
@@ -6679,8 +6704,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (hasSelection()) {
startTextSelectionMode();
- } else if (mInsertionPointCursorController != null) {
- mInsertionPointCursorController.show();
}
}
}
@@ -7645,6 +7668,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private int mPositionY;
private CursorController mController;
private boolean mIsDragging;
+ private int mOffsetX;
+ private int mOffsetY;
public HandleView(CursorController controller, Drawable handle) {
super(TextView.this.mContext);
@@ -7653,6 +7678,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mContainer = new PopupWindow(TextView.this.mContext, null,
com.android.internal.R.attr.textSelectHandleWindowStyle);
mContainer.setSplitTouchEnabled(true);
+ mContainer.setClippingEnabled(false);
+ mContainer.setLayoutInScreenEnabled(true);
}
@Override
@@ -7690,19 +7717,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int compoundPaddingRight = getCompoundPaddingRight();
final TextView hostView = TextView.this;
- final int right = hostView.mRight;
- final int left = hostView.mLeft;
- final int bottom = hostView.mBottom;
- final int top = hostView.mTop;
+ final int handleWidth = mDrawable.getIntrinsicWidth();
+ final int left = 0;
+ final int right = hostView.getWidth();
+ final int top = 0;
+ final int bottom = hostView.getHeight();
- final int clipLeft = left + compoundPaddingLeft;
+ final int clipLeft = left + compoundPaddingLeft - (int) (handleWidth * 0.75f);
final int clipTop = top + extendedPaddingTop;
- final int clipRight = right - compoundPaddingRight;
+ final int clipRight = right - compoundPaddingRight + (int) (handleWidth * 0.25f);
final int clipBottom = bottom - extendedPaddingBottom;
- final int handleWidth = mDrawable.getIntrinsicWidth();
- return mPositionX >= clipLeft - handleWidth * 0.75f &&
- mPositionX <= clipRight + handleWidth * 0.25f &&
+ return mPositionX >= clipLeft && mPositionX <= clipRight &&
mPositionY >= clipTop && mPositionY <= clipBottom;
}
@@ -7741,6 +7767,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ mOffsetX = (int) (ev.getX() - mDrawable.getIntrinsicWidth() / 2.f + 0.5f);
+ mOffsetY = (int) (ev.getY() - mDrawable.getIntrinsicHeight() / 2.f + 0.5f);
mIsDragging = true;
break;
@@ -7749,8 +7777,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final float rawY = ev.getRawY();
final int[] coords = mTempCoords;
TextView.this.getLocationOnScreen(coords);
- final int x = (int) (rawX - coords[0] + 0.5f);
- final int y = (int) (rawY - coords[1] + 0.5f);
+ final int x = (int) (rawX - coords[0] + 0.5f) - mOffsetX;
+ final int y = (int) (rawY - coords[1] + 0.5f) -
+ (int) (mDrawable.getIntrinsicHeight() * 0.8f) - mOffsetY;
+
mController.updatePosition(this, x, y);
break;
@@ -8059,7 +8089,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int previousLine = layout.getLineForOffset(previousOffset);
final int previousLineTop = layout.getLineTop(previousLine);
final int previousLineBottom = layout.getLineBottom(previousLine);
- final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 2;
+ final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 6;
// If new line is just before or after previous line and y position is less than
// hysteresisThreshold away from previous line, keep cursor on previous line.
diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java
index 52f5716..a589fe9 100644
--- a/voip/java/android/net/sip/SipManager.java
+++ b/voip/java/android/net/sip/SipManager.java
@@ -173,7 +173,7 @@ public class SipManager {
SipRegistrationListener listener) throws SipException {
try {
mSipService.open3(localProfile, incomingCallBroadcastAction,
- createRelay(listener));
+ createRelay(listener, localProfile.getUriString()));
} catch (RemoteException e) {
throw new SipException("open()", e);
}
@@ -191,7 +191,7 @@ public class SipManager {
SipRegistrationListener listener) throws SipException {
try {
mSipService.setRegistrationListener(
- localProfileUri, createRelay(listener));
+ localProfileUri, createRelay(listener, localProfileUri));
} catch (RemoteException e) {
throw new SipException("setRegistrationListener()", e);
}
@@ -425,8 +425,8 @@ public class SipManager {
public void register(SipProfile localProfile, int expiryTime,
SipRegistrationListener listener) throws SipException {
try {
- ISipSession session = mSipService.createSession(
- localProfile, createRelay(listener));
+ ISipSession session = mSipService.createSession(localProfile,
+ createRelay(listener, localProfile.getUriString()));
session.register(expiryTime);
} catch (RemoteException e) {
throw new SipException("register()", e);
@@ -446,8 +446,8 @@ public class SipManager {
public void unregister(SipProfile localProfile,
SipRegistrationListener listener) throws SipException {
try {
- ISipSession session = mSipService.createSession(
- localProfile, createRelay(listener));
+ ISipSession session = mSipService.createSession(localProfile,
+ createRelay(listener, localProfile.getUriString()));
session.unregister();
} catch (RemoteException e) {
throw new SipException("unregister()", e);
@@ -475,8 +475,8 @@ public class SipManager {
}
private static ISipSessionListener createRelay(
- SipRegistrationListener listener) {
- return ((listener == null) ? null : new ListenerRelay(listener));
+ SipRegistrationListener listener, String uri) {
+ return ((listener == null) ? null : new ListenerRelay(listener, uri));
}
/**
@@ -512,16 +512,18 @@ public class SipManager {
private static class ListenerRelay extends SipSessionAdapter {
private SipRegistrationListener mListener;
+ private String mUri;
// listener must not be null
- public ListenerRelay(SipRegistrationListener listener) {
+ public ListenerRelay(SipRegistrationListener listener, String uri) {
mListener = listener;
+ mUri = uri;
}
private String getUri(ISipSession session) {
try {
return ((session == null)
- ? "no session"
+ ? mUri
: 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 130fe9f..aff1439 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -39,6 +39,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -49,6 +50,7 @@ import java.io.IOException;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
@@ -119,17 +121,19 @@ public final class SipService extends ISipService.Stub {
}
public synchronized SipProfile[] getListOfProfiles() {
- SipProfile[] profiles = new SipProfile[mSipGroups.size()];
- int i = 0;
+ boolean isCallerRadio = isCallerRadio();
+ ArrayList<SipProfile> profiles = new ArrayList<SipProfile>();
for (SipSessionGroupExt group : mSipGroups.values()) {
- profiles[i++] = group.getLocalProfile();
+ if (isCallerRadio || isCallerCreator(group)) {
+ profiles.add(group.getLocalProfile());
+ }
}
- return profiles;
+ return profiles.toArray(new SipProfile[profiles.size()]);
}
public void open(SipProfile localProfile) {
localProfile.setCallingUid(Binder.getCallingUid());
- if (localProfile.getAutoRegistration()) {
+ if (localProfile.getAutoRegistration() && isCallerRadio()) {
openToReceiveCalls(localProfile);
} else {
openToMakeCalls(localProfile);
@@ -153,8 +157,14 @@ public final class SipService extends ISipService.Stub {
String incomingCallBroadcastAction, ISipSessionListener listener) {
localProfile.setCallingUid(Binder.getCallingUid());
if (TextUtils.isEmpty(incomingCallBroadcastAction)) {
- throw new RuntimeException(
- "empty broadcast action for incoming call");
+ Log.w(TAG, "empty broadcast action for incoming call");
+ return;
+ }
+ if (incomingCallBroadcastAction.equals(
+ SipManager.ACTION_SIP_INCOMING_CALL) && !isCallerRadio()) {
+ Log.w(TAG, "failed to open the profile; "
+ + "the action string is reserved");
+ return;
}
if (DEBUG) Log.d(TAG, "open3: " + localProfile.getUriString() + ": "
+ incomingCallBroadcastAction + ": " + listener);
@@ -171,29 +181,64 @@ public final class SipService extends ISipService.Stub {
}
}
+ private boolean isCallerCreator(SipSessionGroupExt group) {
+ SipProfile profile = group.getLocalProfile();
+ return (profile.getCallingUid() == Binder.getCallingUid());
+ }
+
+ private boolean isCallerCreatorOrRadio(SipSessionGroupExt group) {
+ return (isCallerRadio() || isCallerCreator(group));
+ }
+
+ private boolean isCallerRadio() {
+ return (Binder.getCallingUid() == Process.PHONE_UID);
+ }
+
public synchronized void close(String localProfileUri) {
- SipSessionGroupExt group = mSipGroups.remove(localProfileUri);
- if (group != null) {
- notifyProfileRemoved(group.getLocalProfile());
- group.close();
- if (isWifiOn() && !anyOpened()) releaseWifiLock();
+ SipSessionGroupExt group = mSipGroups.get(localProfileUri);
+ if (group == null) return;
+ if (!isCallerCreatorOrRadio(group)) {
+ Log.d(TAG, "only creator or radio can close this profile");
+ return;
}
+
+ group = mSipGroups.remove(localProfileUri);
+ notifyProfileRemoved(group.getLocalProfile());
+ group.close();
+ if (isWifiOn() && !anyOpened()) releaseWifiLock();
}
public synchronized boolean isOpened(String localProfileUri) {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- return ((group != null) ? group.isOpened() : false);
+ if (group == null) return false;
+ if (isCallerCreatorOrRadio(group)) {
+ return group.isOpened();
+ } else {
+ Log.i(TAG, "only creator or radio can query on the profile");
+ return false;
+ }
}
public synchronized boolean isRegistered(String localProfileUri) {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- return ((group != null) ? group.isRegistered() : false);
+ if (group == null) return false;
+ if (isCallerCreatorOrRadio(group)) {
+ return group.isRegistered();
+ } else {
+ Log.i(TAG, "only creator or radio can query on the profile");
+ return false;
+ }
}
public synchronized void setRegistrationListener(String localProfileUri,
ISipSessionListener listener) {
SipSessionGroupExt group = mSipGroups.get(localProfileUri);
- if (group != null) group.setListener(listener);
+ if (group == null) return;
+ if (isCallerCreator(group)) {
+ group.setListener(listener);
+ } else {
+ Log.i(TAG, "only creator can set listener on the profile");
+ }
}
public synchronized ISipSession createSession(SipProfile localProfile,
@@ -234,6 +279,8 @@ public final class SipService extends ISipService.Stub {
group = new SipSessionGroupExt(localProfile, null, null);
mSipGroups.put(key, group);
notifyProfileAdded(localProfile);
+ } else if (!isCallerCreator(group)) {
+ throw new SipException("only creator can access the profile");
}
return group;
}
@@ -244,6 +291,9 @@ public final class SipService extends ISipService.Stub {
String key = localProfile.getUriString();
SipSessionGroupExt group = mSipGroups.get(key);
if (group != null) {
+ if (!isCallerCreator(group)) {
+ throw new SipException("only creator can access the profile");
+ }
group.setIncomingCallBroadcastAction(
incomingCallBroadcastAction);
group.setListener(listener);
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index f09edb6..a1caa1f 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -57,9 +57,9 @@ int gRandom = -1;
// a modulo operation on the index while accessing the array. However modulo can
// be expensive on some platforms, such as ARM. Thus we round up the size of the
// array to the nearest power of 2 and then use bitwise-and instead of modulo.
-// Currently we make it 256ms long and assume packet interval is 32ms or less.
-// The first 64ms is the place where samples get mixed. The rest 192ms is the
-// real jitter buffer. For a stream at 8000Hz it takes 4096 bytes. These numbers
+// Currently we make it 512ms long and assume packet interval is 40ms or less.
+// The first 80ms is the place where samples get mixed. The rest 432ms is the
+// real jitter buffer. For a stream at 8000Hz it takes 8192 bytes. These numbers
// are chosen by experiments and each of them can be adjusted as needed.
// Other notes:
@@ -69,7 +69,11 @@ int gRandom = -1;
// milliseconds. No floating points.
// + If we cannot get enough CPU, we drop samples and simulate packet loss.
// + Resampling is not done yet, so streams in one group must use the same rate.
-// For the first release we might only support 8kHz and 16kHz.
+// For the first release only 8000Hz is supported.
+
+#define BUFFER_SIZE 512
+#define HISTORY_SIZE 80
+#define MEASURE_PERIOD 2000
class AudioStream
{
@@ -111,6 +115,7 @@ private:
int mBufferMask;
int mBufferHead;
int mBufferTail;
+ int mLatencyTimer;
int mLatencyScore;
uint16_t mSequence;
@@ -159,12 +164,13 @@ bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
mInterval = mSampleCount / mSampleRate;
// Allocate jitter buffer.
- for (mBufferMask = 8192; mBufferMask < sampleRate; mBufferMask <<= 1);
- mBufferMask >>= 2;
+ for (mBufferMask = 8; mBufferMask < mSampleRate; mBufferMask <<= 1);
+ mBufferMask *= BUFFER_SIZE;
mBuffer = new int16_t[mBufferMask];
--mBufferMask;
mBufferHead = 0;
mBufferTail = 0;
+ mLatencyTimer = 0;
mLatencyScore = 0;
// Initialize random bits.
@@ -340,29 +346,35 @@ void AudioStream::decode(int tick)
}
// Make sure mBufferHead and mBufferTail are reasonable.
- if ((unsigned int)(tick + 256 - mBufferHead) > 1024) {
- mBufferHead = tick - 64;
+ if ((unsigned int)(tick + BUFFER_SIZE - mBufferHead) > BUFFER_SIZE * 2) {
+ mBufferHead = tick - HISTORY_SIZE;
mBufferTail = mBufferHead;
}
- if (tick - mBufferHead > 64) {
+ if (tick - mBufferHead > HISTORY_SIZE) {
// Throw away outdated samples.
- mBufferHead = tick - 64;
+ mBufferHead = tick - HISTORY_SIZE;
if (mBufferTail - mBufferHead < 0) {
mBufferTail = mBufferHead;
}
}
- if (mBufferTail - tick <= 80) {
- mLatencyScore = tick;
- } else if (tick - mLatencyScore >= 5000) {
- // Reset the jitter buffer to 40ms if the latency keeps larger than 80ms
- // in the past 5s. This rarely happens, so let us just keep it simple.
- LOGD("stream[%d] latency control", mSocket);
- mBufferTail = tick + 40;
+ // Adjust the jitter buffer if the latency keeps larger than two times of the
+ // packet interval in the past two seconds.
+ int score = mBufferTail - tick - mInterval * 2;
+ if (mLatencyScore > score) {
+ mLatencyScore = score;
+ }
+ if (mLatencyScore <= 0) {
+ mLatencyTimer = tick;
+ mLatencyScore = score;
+ } else if (tick - mLatencyTimer >= MEASURE_PERIOD) {
+ LOGD("stream[%d] reduces latency of %dms", mSocket, mLatencyScore);
+ mBufferTail -= mLatencyScore;
+ mLatencyTimer = tick;
}
- if (mBufferTail - mBufferHead > 256 - mInterval) {
+ if (mBufferTail - mBufferHead > BUFFER_SIZE - mInterval) {
// Buffer overflow. Drop the packet.
LOGD("stream[%d] buffer overflow", mSocket);
recv(mSocket, &c, 1, MSG_DONTWAIT);
@@ -413,17 +425,17 @@ void AudioStream::decode(int tick)
}
if (tick - mBufferTail > 0) {
- // Buffer underrun. Reset the jitter buffer to 40ms.
+ // Buffer underrun. Reset the jitter buffer.
LOGD("stream[%d] buffer underrun", mSocket);
if (mBufferTail - mBufferHead <= 0) {
- mBufferHead = tick + 40;
+ mBufferHead = tick + mInterval;
mBufferTail = mBufferHead;
} else {
- int tail = (tick + 40) * mSampleRate;
+ int tail = (tick + mInterval) * mSampleRate;
for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) {
mBuffer[i & mBufferMask] = 0;
}
- mBufferTail = tick + 40;
+ mBufferTail = tick + mInterval;
}
}
@@ -680,7 +692,7 @@ bool AudioGroup::NetworkThread::threadLoop()
int count = 0;
for (AudioStream *stream = chain; stream; stream = stream->mNext) {
- if (!stream->mTick || tick - stream->mTick >= 0) {
+ if (tick - stream->mTick >= 0) {
stream->encode(tick, chain);
}
if (deadline - stream->mTick > 0) {